Railway Operation Simulator  v2.11.1
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 #include "DisplayUnit.h"
49 #include "Utilities.h"
50 
51 // ---------------------------------------------------------------------------
52 #pragma package(smart_init)
53 
55 
56 // ---------------------------------------------------------------------------
57 
58 int TTrain::NextTrainID = 0; // has to be initialised outside the class
59 
60 // ---------------------------------------------------------------------------
61 
62 TExitInfo::TExitInfo() //default constructor
63 {
64  ServiceReference = " ";
65  RepeatNumber = 0;
66  TimeToExitSecs = -1;
67 }
68 
69 // ---------------------------------------------------------------------------
70 
71 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
72  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
73  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
74  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
75  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
76  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
77 /*
78  Construct a new train with general default values and input values for position and headcode.
79  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
80  This is because trains are kept in a vector and vectors erase elements during internal operations.
81  Deletion is explicit by using a special function. Increment the static class member NextTrainID
82  after setting this train's ID.
83 */
84 
85 {
86  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
87  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
88  AnsiString(TrainModeIn));
89  // AutoControl = true;//all trains start in auto control
90  UpdateCounter = 0;
91  TimeTimeLocArrived = false;
92  Derailed = false;
93  DerailPending = false;
94  Crashed = false;
95  StoppedAtBuffers = false;
96  StoppedAtSignal = false;
97  StoppedAtLocation = false;
98  StoppedAfterSPAD = false;
99  StoppedWithoutPower = false; // new at v2.4.0
100  StoppedForTrainInFront = false;
101  SignallerStoppingFlag = false;
102  SignallerStopped = false;
103  SignallerRemoved = false;
104  NotInService = false;
105  HoldAtLocationInTTMode = false;
106  AllowedToPassRedSignal = false;
107  CallingOnFlag = false;
108  BeingCalledOn = false;
109  DepartureTimeSet = false;
111  TimetableFinished = false;
112  LastActionDelayFlag = false;
113  OneLengthAccelDecel = false;
114  TrainCrashedInto = -1;
116  Plotted = false;
117  TrainGone = false;
118  SPADFlag = false;
119  FrontCodePtr = new Graphics::TBitmap;
120  FrontCodePtr->PixelFormat = pf8bit;
121  FrontCodePtr->Height = 8;
122  FrontCodePtr->Width = 8;
124  FrontCodePtr->Transparent = false;
125  AValue = sqrt(2 * PowerAtRail / Mass);
127  TerminatedMessageSent = false;
128  JoinedOtherTrainFlag = false;
130  StepForwardFlag = false;
132  for(int x = 0; x < 4; x++)
133  {
134  HeadCodeGrPtr[x] = new Graphics::TBitmap;
135  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
136  HeadCodeGrPtr[x]->Height = 8;
137  HeadCodeGrPtr[x]->Width = 8;
139  HeadCodeGrPtr[x]->Transparent = false;
140  }
141  for(int x = 0; x < 4; x++)
142  {
143  BackgroundPtr[x] = new Graphics::TBitmap;
144  BackgroundPtr[x]->PixelFormat = pf8bit;
145  BackgroundPtr[x]->Height = 8;
146  BackgroundPtr[x]->Width = 8;
148  BackgroundPtr[x]->Transparent = false;
149  }
150  for(int x = 0; x < 4; x++)
151  {
153  // set here to ensure have values
154  }
155  for(int x = 0; x < 4; x++)
156  {
157  PlotElement[x] = -1; // marker for not plotted yet
158  }
159  for(int x = 0; x < 3; x++)
160  {
161  OldZoomOutElement[x] = -1; // marker for not plotted yet
162  }
164  NextTrainID++;
165 
166  // new values added to complete initialisation of all TTrain variables
167 
168  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
169  // TrainDataEntryPtr, initialise in AddTrain
171  FrontElementLength = 0;
172  EntrySpeed = 0;
173  ExitSpeedHalf = 0;
174  ExitSpeedFull = 0;
175  MaxExitSpeed = 0;
176  BrakeRate = 0;
178  FirstHalfMove = true;
179  EntryTime = 0;
180  ExitTimeHalf = 0;
181  ExitTimeFull = 0;
182  ReleaseTime = 0;
183  TRSTime = 0;
184  LastActionTime = 0;
185  Straddle = MidLag;
186  LeadElement = -1;
187  LeadEntryPos = 0;
188  LeadExitPos = 0;
189  MidElement = -1;
190  MidEntryPos = 0;
191  MidExitPos = 0;
192  LagElement = -1;
193  LagEntryPos = 0;
194  LagExitPos = 0;
195  TrainFailed = false; // added at v2.4.0
196  for(int x = 0; x < 4; x++)
197  {
198  HOffset[x] = 0;
199  VOffset[x] = 0;
200  PlotEntryPos[x] = 0;
201  }
202  OpTimeToAct = 60; // default value, new at v2.2.0
203  TimeToExit = -1;
204  ExitPair.first = -1;
205  ExitPair.second = -1;
206  MinsDelayed = 0.0; // new at v2.2.0
207  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
208  FinishJoinLogSent = false;
209  // added at v2.4.0 to prevent repeatdly logging the event
212  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
216  ZeroPowerNoCDTMessage = false;
221  TrainFailurePending = false;
222  SkippedDeparture = false;
223  ActionsSkippedFlag = false;
224  SkipPtrValue = 0;
225  TrainSkippedEvents = 0;
226  Utilities->CallLogPop(648);
227 }
228 
229 // ---------------------------------------------------------------------------
230 
231 void TTrain::DeleteTrain(int Caller)
232 /*
233  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
234  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
235  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
236  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
237  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
238  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
239  No need to delete HeadCodePosition as that just points to existing bitmaps
240 */{
241  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
242  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
243  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
244  if(Display->ZoomOutFlag)
245  {
247  }
248  if(FrontCodePtr == 0)
249  {
250  throw Exception("Error in attempting to delete FrontCodePtr");
251  }
252  delete FrontCodePtr;
253  FrontCodePtr = 0;
254  for(int x = 0; x < 4; x++)
255  {
256  if(BackgroundPtr[x] == 0)
257  {
258  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
259  }
260  delete BackgroundPtr[x];
261  BackgroundPtr[x] = 0;
262  }
263  for(int x = 0; x < 4; x++)
264  {
265  if(HeadCodeGrPtr[x] == 0)
266  {
267  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
268  }
269  delete HeadCodeGrPtr[x];
270  HeadCodeGrPtr[x] = 0;
271  }
272  Utilities->CallLogPop(649);
273 }
274 
275 // ---------------------------------------------------------------------------
276 
278 /*
279  Plots the train starting position on screen. Note that the check for starting on straight points &
280  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
281  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
282  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
283  Set the headcode graphics pointers from the headcode text, then check whether starting at a
284  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
285  for the continuation element. Otherwise set Lead and Mid values,
286 
287  and Lead element value unless
288  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
289  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
290  then check if a train on either Mid or Lag and if so give a warning message and return false so
291  that the calling function can delete the train. Plot the Mid element train values then do similarly
292  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
293  the train. Finally set the Plotted flag and return true.
294 */{
295  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
296  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
297 
299  // PlotStartTime = TrainController->TTClockTime;
300  FirstHalfMove = true;
301 
302  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
303  // 'claim' it for this train to prevent any other waiting trains trying to enter
305  {
306  LagElement = -1; // not to be plotted
307  LagExitPos = 0; // not to be plotted
308  LagEntryPos = 0; // not to be plotted
309  MidElement = -1; // not to be plotted
310  MidExitPos = 0; // not to be plotted
311  MidEntryPos = 0; // not to be plotted
313  LeadExitPos = 1; // will be 1 for continuation entry
314  LeadEntryPos = 0;
315 
317  MaxExitSpeed = StartSpeed; // initial value
319  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
320  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
321  if(EntrySpeed > SpeedLimit)
322  {
323  EntrySpeed = SpeedLimit;
324  }
326  {
328  }
330  // LeadElement is the element to be entered
331 
332  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
333  // can achieve ExitSpeedFull at the half braking rate.
335  {
336  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
337  if(TempEntrySpeed < EntrySpeed)
338  {
339  EntrySpeed = TempEntrySpeed;
341  }
342  }
343  Straddle = MidLag; // only for starting on a continuation
345  // no need to stop gap flashing if start on continuation
346  }
347  else // not starting at a continuation
348  {
349  LagElement = -1;
350  LagEntryPos = 0;
351  LagExitPos = 0;
358 
360  MaxExitSpeed = StartSpeed; // initial value
362  bool TempDerail = false; // dummy
363  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
365  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
366  {
367  StoppedWithoutPower = true;
368  }
369  // facing buffers check - ignore starting speed if start facing buffers
370  StoppedAtBuffers = false;
371  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
374  {
375  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
376  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
377  EntrySpeed = 0;
378  ExitSpeedHalf = 0;
379  ExitSpeedFull = 0;
380  MaxExitSpeed = 0;
381  // SetTrainMovementValues not called so set this here
382  BrakeRate = 0;
385  StoppedAtSignal = false;
386  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
387  // signal check is an 'else'
388  if(!StoppedAtLocation)
389  {
390  StoppedAtBuffers = true; // stopped at location takes precedence
391  }
392  }
393 
394  // facing continuation check - don't allow to stop even if no power
396  {
397  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
398  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
402  BrakeRate = 0;
403  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
404  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
405  }
406 
407  // Signal check
408  else if((NextElementPosition > -1) && (NextEntryPos > -1))
409  // condition check added as precaution after SloughIECC error reported by James U
410  {
411  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
412  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
413  {
414  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
415  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
416  EntrySpeed = 0;
417  ExitSpeedHalf = 0;
418  ExitSpeedFull = 0;
419  MaxExitSpeed = 0;
420  BrakeRate = 0;
423  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
424  {
425  StoppedAtSignal = true;
427  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
428  }
430  {
431  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass red signal' is offered in popup menu rather than move
432  // forwards, but don't change the background colour so still shows as stopped at location
433  StoppedAtSignal = true;
434  }
435  }
436  else
437  {
438  StoppedAtSignal = false;
439  if(NextEntryPos > 1)
440  {
441  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
442  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
443  }
444  else
445  {
446  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
447  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
448  }
449  if(EntrySpeed > SpeedLimit)
450  {
451  EntrySpeed = SpeedLimit;
452  }
454  {
456  }
458  TDateTime TestTime = TrainController->TTClockTime; // test
459  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
460  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
461  // NextElement is the element to be entered
462 
463  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
464  // can achieve ExitSpeedFull at the half braking rate.
466  {
467  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
468  // half braking
469  if(TempEntrySpeed < EntrySpeed)
470  {
471  EntrySpeed = TempEntrySpeed;
472  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
473  }
474  }
475  }
476  }
478  {
479  throw Exception("Error, LeadElement Exit Connection is NotSet");
480  }
481  }
482  if(MidElement > -1) // will be -1 if start on continuation
483  {
484  Straddle = LeadMid;
488  {
489  for(int x = 0; x < 4; x++)
490  {
491  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
492  }
493  }
494  else
495  {
496  for(int x = 0; x < 4; x++)
497  {
499  }
500  }
501  if(TrainMode == Timetable)
502  {
504  }
505  else
506  {
508  }
510  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
511 
514 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
515  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
516  {
517  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
518  Utilities->CallLogPop(651);
519  return false;
520  }
521 */
526  PlotTrainGraphic(8, 0, Display);
527  PlotTrainGraphic(9, 1, Display);
528 
531 
532  // pick up background bitmaps [2] & [3]
533 
536 
537  PlotElement[2] = MidElement;
539  PlotElement[3] = MidElement;
541  PlotTrainGraphic(10, 2, Display);
542  PlotTrainGraphic(11, 3, Display);
543  // Plotted = true; set in PlotTrainGraphic
544  }
545  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
546  Utilities->CallLogPop(652);
547 }
548 
549 // ---------------------------------------------------------------------------
550 void TTrain::UnplotTrain(int Caller)
551 {
552  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
553  if(!Plotted)
554  {
555  return;
556  }
557  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
558 
559  if(Straddle == MidLag)
560  {
561  if(MidElement > -1)
562  {
567  // to force plot of locked route marker, needed once only for the element
568  }
569  if(LagElement > -1)
570  {
575  // to force plot of locked route marker, needed once only for the element
576  }
577  }
578  else if(Straddle == LeadMidLag)
579  {
580  if(LeadElement > -1)
581  {
584  // to force plot of locked route marker, needed once only for the element
585  }
586  if(MidElement > -1)
587  {
592  // to force plot of locked route marker, needed once only for the element
593  }
594  if(LagElement > -1)
595  {
598  // to force plot of locked route marker, needed once only for the element
599  }
600  }
601  else if(Straddle == LeadMid)
602  {
603  if(LeadElement > -1)
604  {
609  // to force plot of locked route marker, needed once only for the element
610  }
611  if(MidElement > -1)
612  {
617  // to force plot of locked route marker, needed once only for the element
618  }
619  }
620  if(LeadElement > -1)
621  {
623  }
624  if(MidElement > -1)
625  {
627  }
628  if(LagElement > -1)
629  {
631  }
632  Plotted = false;
634  Display->Update();
635  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
636  // resurrected when Update() dropped from PlotOutput etc
637  Utilities->CallLogPop(653);
638 }
639 
640 // ----------------------------------------------------------------------------
641 
642 void TTrain::UpdateTrain(int Caller)
643 /*
644  Note: Some changes made since comments written
645 
646  Brief:
647  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
648  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
649  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
650  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
651  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
652  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
653  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
654  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
655  changed to MidLag within the function and all elements moved down one, old Mid becomes
656  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
657  incremented to reflect the position the train now occupies.
658 
659  Detail:
660  Set TrainFailurePending if all conditions met
661  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
662  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
663  and return.
664  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
665  If there's a LagElement (there will be but include check for good practice - next
666  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
667  train fully on offending point - Derail set and DerailPanding reset, train background
668  colour changed (note that BackgroundColour is a property of the train itself) then return.
669  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
670  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
671  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
672  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
673  if LeadElement is a fouled trailing point.
674  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
675  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
676  replotting the last background segment and checking whether the element is a bridge or crossover with the other
677  track in a route, in which case the route colour is replotted.
678  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
679  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
680  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
681  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
682  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
683  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
684  train can be deleted by the calling function, and the function returns.
685  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
686  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
687  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
688  basic red aspect.
689 
690  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
691  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
692  regardless of direction, and with the correct front code colour.
693 
694  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
695  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
696  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
697  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
698  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
699  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
700  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
701 
702  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
703  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
704  changed similarly. The function then returns.
705 
706  If Crashed is not set then Straddle is incremented and the function returns.
707 */
708 
709 {
710  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
711  UpdateCounter++;
712  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
713  if(UpdateCounter >= 100)
714  {
715  UpdateCounter = 0;
716  }
717  int RandRange = (TrainController->MTBFHours * 3600) / 53;
718 
719  // MTBFHours is in timetable clock hours, min value is 1 & max value is 10,000 (integer values on input)
720  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
721  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
722  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
723  // RandomFailureCounter value is fixed for a full cycle of train updates so this
724  // makes sure there's no bunching of failures as there is for a fixed comparison number
725  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
726  // gives a random number between 0 and 32767 (defined as RAND_MAX in stdlib.h)
727  {
728  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
729  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
730  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
731  // don't fail if:
732  // (a) on a continuation (entering or leaving);
733  // (b) already failed;
734  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
735  // (d) train terminated;
736  // (e) crashed or derailed; or
737  // (f) under signaller control and stopped.
738  {
739  if((random(RandRange)) == 0)
740  // max value for RandRange is over 2x10^9
741  {
742  // here if failure due
743  TrainFailurePending = true;
744  // fail when PlotElements set to proper Lead & Mid Elements
745  }
746  }
747  }
748 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
749  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
750  {
751  StoppedWithoutPower = true;
752  }
753 */
754 // float TimeToExit; //added at v2.10.0 Removed these so original values retained - used when train on continuation
755 // THVShortPair ExitPair; //added at v2.10.0
756  int LockedVectorNumber;
757  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
758  // default values - these needed for route checker below
759  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
760 
762  {
764  }
765  if(Crashed || Derailed)
766  {
768  {
769  PlotTrain(7, Display);
770  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
771  Display->Update();
772  }
773  OpTimeToAct = 0.0;
774  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
775  Utilities->CallLogPop(1017);
776  return; // no further action, user has to remove or work around
777  }
779  {
781  }
783  {
785  }
787  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
788  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
789  // to move & then stop again at the same station
790  {
791  TimeTimeLocArrived = false;
792  }
793  if(!Stopped() && !SPADFlag && !TrainFailed)
794  {
796  }
797  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
798  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
799 /* old version where force a stop at buffers regardless of speed
800  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
801  else StoppedAtBuffers = false;
802 */
803 
804  // new version where crash if run into buffers
805  if(!Crashed)
806  {
807  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
808  {
809  if(ExitSpeedFull > 1)
810  {
811  Crashed = true;
815  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
816  // no need for missed action logs - will be sent when train removed
817  StoppedAtBuffers = false;
818  }
820  // stopped at location & stopped without power take precedence
821  {
822  StoppedAtBuffers = true;
823  }
824  else
825  {
826  StoppedAtBuffers = false;
827  }
828  }
829  else
830  {
831  StoppedAtBuffers = false;
832  }
833  }
834  else
835  {
836  StoppedAtBuffers = false;
837  }
838  // if crashed don't want stopped at buffers set
839 
840  // also crash if run into a level crossing that is changing or has barriers up
841  if(!Crashed)
842  {
843  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
844  {
845  int H = Track->TrackElementAt(873, LeadElement).HLoc;
846  int V = Track->TrackElementAt(874, LeadElement).VLoc;
847  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
848  {
849  Crashed = true;
853  // no need for missed action logs - will be sent when train removed
854  }
855  }
856  }
858  {
860  }
861  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
862  if((TrainMode == Timetable) && StoppedAtLocation && (ActionVectorEntryPtr->Command != "")) //if Command == "" then either TimeLoc or TimeTimeLoc
863  {
864  HoldAtLocationInTTMode = true;
865  }
866  else if(TrainMode == Timetable)
867  {
868  HoldAtLocationInTTMode = false;
869  }
870  // in Signaller mode HoldAtLocationInTTMode not changed
871 
872  // check if departure pending & set times unless already set
873  if(TrainMode == Timetable)
874  {
876  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
877  {
878  if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1))
879  {
881  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
882  {
883  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
884  }
885  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
886  DepartureTimeSet = true;
887  }
888  }
889  }
890  if(TrainController->OpTimeToActUpdateCounter == 0)// && TrainController->OpActionPanelVisible) removed last condition so always calc TimeToExit
891  {
892  OpTimeToAct = CalcTimeToAct(0, TimeToExit, ExitPair); // called after ReleaseTime set
893 // this->TimeToExit = TimeToExit; don't need these as values updated directly
894 // this->ExitPair = ExitPair;
895  // calculate every 1 sec (in real time, not timetable time) for all trains
896  }
897  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
898  if(TrainMode == Timetable)
899  {
900  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
901  {
902  RemainHereLogNotSent = true;
903  }
905  {
906  // ignore TimeLoc & TTLoc departures
907  // Action logs given in functions
909  LastActionTime + TDateTime(30.0 / 86400)))
910  {
911  if(ActionVectorEntryPtr->Command == "fsp")
912  {
913  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
914  FrontTrainSplit(0);
915  if(TrainFailurePending) // ok, stopped so PlotElements set
916  {
917  TrainHasFailed(0);
918  }
919  Utilities->CallLogPop(2041);
920  return;
921  }
922  else if(ActionVectorEntryPtr->Command == "rsp")
923  {
924  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
925  RearTrainSplit(0);
926  if(TrainFailurePending) // ok, stopped so PlotElements set
927  {
928  TrainHasFailed(1);
929  }
930  Utilities->CallLogPop(2042);
931  return;
932  }
933  else if(ActionVectorEntryPtr->Command == "Fjo")
934  {
935  FinishJoin(0);
936  }
937  else if(ActionVectorEntryPtr->Command == "jbo")
938  {
939  JoinedBy(0);
940  }
941  else if(ActionVectorEntryPtr->Command == "cdt")
942  {
944  }
945  else if(ActionVectorEntryPtr->Command == "Fns")
946  {
947  NewTrainService(0);
948  }
949  else if(ActionVectorEntryPtr->Command == "Frh")
950  {
951  RemainHere(0);
952  }
953  else if(ActionVectorEntryPtr->Command == "Fer")
954  {
955  TimetableFinished = true;
956  }
957  // other aspects of 'Fer' dealt with in TTrain::Update()
958  else if(ActionVectorEntryPtr->Command == "F-nshs")
959  {
961  }
962  else if(ActionVectorEntryPtr->Command == "Frh-sh")
963  {
965  }
966  else if(ActionVectorEntryPtr->Command == "Fns-sh")
967  {
969  }
970 /*
971  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
972  shuttle headcode (no train creation)
973  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
974  remain here
975  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
976  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
977 */
978  }
979  }
980  else
981  {
983  {
985  }
986  }
987  }
988  if(TrainMode == Timetable)
989  {
990  if(StoppedAtBuffers)
991  {
992  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
993  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
994  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
995  if(BufferLocation == "")
996  {
998  }
999  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
1000  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
1001  {
1005  {
1007  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1008  // Drop missed actions so user can still use sig mode to get back on track
1010  }
1011  if(TrainFailurePending) // ok, stopped so PlotElements set
1012  {
1014  TrainHasFailed(2);
1015  }
1016  Utilities->CallLogPop(1020);
1017  return;
1018  }
1019  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !StoppedAtLocation && (TrainController->TTClockTime >
1020  ReleaseTime))
1021  {
1024  {
1027  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1028  // Drop missed actions so user can still use sig mode to get back on track
1030  }
1031  if(TrainFailurePending) // ok, stopped so PlotElements set
1032  {
1034  TrainHasFailed(3);
1035  }
1036  Utilities->CallLogPop(1397);
1037  return;
1038  }
1039  }
1040  else
1041  {
1043  }
1044  }
1045  else
1046  {
1048  }
1049  if(TrainMode == Timetable)
1050  {
1052  {
1054  }
1056  {
1058  }
1059  }
1060  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1061  // restart after stopped for train in front
1062  int NextElementPosition, NextEntryPos;
1063 
1064  if(LeadElement > -1) // if an exit continuation then not set
1065  {
1066  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1067  {
1069  }
1070  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1071  {
1072  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1073  {
1074  LeadExitPos = 1;
1075  }
1076  else
1077  {
1078  LeadExitPos = 3;
1079  }
1080  }
1081  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1082  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1083  }
1084  else
1085  {
1086  NextElementPosition = -1;
1087  NextEntryPos = -1;
1088  }
1089  if((NextElementPosition > -1) && (NextEntryPos > -1))
1090  // may be buffers or continuation so need this check
1091  {
1092 /*
1093  Check whether calling-on conditions met:-
1094  a) approaching train has stopped at a signal but not at a location;
1095  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1096  change of direction (cdt), remaining here (Frh), or under signaller control);
1097  c) at least 1 platform available for the approaching train;
1098  d) points (if any) set for direct route into platform;
1099  e) approaching train is to stop at station;
1100  f) no more facing signals between train and platform;
1101  g) [dropped g]
1102  h) train in front preventing route being set far enough to release stop signal;
1103  i) train in front not exiting at continuation;
1104  j) signal must be within 4km of the stop platform;
1105  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily];
1106  l) no existing route conflicts with the route into the platform; and
1107  m) not failed or without power (these added at v2.10.0)
1108  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1109  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1110 */
1111  if(TrainMode == Timetable)
1112  {
1113  if(CallingOnAllowed(0)) //returns false if failed or no power (modified afer v2.9.2)
1114  {
1115  CallingOnFlag = true;
1116  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1117  }
1118  else
1119  {
1120  if(CallingOnFlag) //TrainHasFailed sets this flag to false (at v2.10.0)
1121  {
1122  if(!TrainFailed) //shouldn't be needed but include for safety at v2.10.0
1123  {
1125  }
1126  }
1127  CallingOnFlag = false;
1128  }
1129  }
1130  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !StoppedAtLocation)
1131  {
1132  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1133  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1134  // sets StoppedAtSignal again & train doesn't move
1135  StoppedAtSignal = false;
1136  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1137  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1138  // LeadMidLag and front of train was on LeadElement (after the current move)
1140  EntrySpeed = 0;
1142  FirstHalfMove = true;
1143  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1144  // NextElement is the element to be entered
1145  }
1147  {
1148  if(ClearToNextSignal(0))
1149  {
1150  StoppedForTrainInFront = false;
1151  BeingCalledOn = false;
1152  EntrySpeed = 0;
1154  FirstHalfMove = true;
1155  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1156  }
1157  else
1158  {
1159  if(TrainFailurePending) // ok, stopped so PlotElements set
1160  {
1161  TrainHasFailed(4);
1162  }
1163  Utilities->CallLogPop(1097);
1164  return;
1165  }
1166  }
1167  }
1168  if((Straddle == MidLag) && (LeadElement != -1))
1169  // later check only for Straddle == LeadMid, so need this check here for initial train start
1170  {
1172  }
1173 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1174  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1175  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1176  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1177  which could be when start as Snt.
1178  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1179  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1180  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1181  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1182  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1183  reached.
1184  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1185  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1186  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1187  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1188  sending a message to the performancelog.
1189 */
1190 
1191  if(TrainMode == Timetable)
1192  {
1194  {
1195  if(BeingCalledOn)
1196  {
1197  StoppedForTrainInFront = true;
1198  }
1200  {
1202  }
1204  {
1205  // value updated at every scheduled departure & arrival
1207  AnsiString StationName;
1209  {
1211  }
1213  {
1215  }
1216  else
1217  {
1218  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1219  }
1220  EntrySpeed = 0;
1224  FirstHalfMove = true;
1225  StoppedAtLocation = false;
1226 
1227  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1228  {
1229  StoppedWithoutPower = true;
1230  }
1231  if((NextElementPosition > -1) && (NextEntryPos > -1))
1232  // condition check added for SloughIECC error reported by James U
1233  {
1234  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1235  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1236  {
1237  StoppedAtSignal = true;
1238  if(!StoppedWithoutPower)
1239  // if stopped without power just keep existing background colour
1240  {
1242  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1243  }
1244  }
1245  }
1247  {
1248  TimeTimeLocArrived = false;
1249  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1250  // no warning for TimeTimeLoc departure
1251  }
1252  else
1253  {
1255  }
1256  DepartureTimeSet = false;
1257  // no need to set LastActionTime for a departure
1258  //deal here with departure pointer change, increment if SkippedDeparture
1259  if(SkippedDeparture)
1260  {
1261  TrainController->SkippedTTEvents += TrainSkippedEvents; //TrainSkippedEvents is 1 less than PassNum
1263  TrainSkippedEvents = 0;
1264  SkippedDeparture = false;
1265  SkipPtrValue = 0;
1266  ActionsSkippedFlag = false;
1267  }
1268  else
1269  {
1271  }
1272  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1273  // note
1274 /*
1275  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1276  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1277  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1278  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1279  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1280  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1281  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1282 */
1284  {
1285  StoppedAtBuffers = true;
1286  }
1287  else if(!StoppedWithoutPower)
1288  // if buffers or no power, don't set values
1289  {
1291  {
1292  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1293  // NextElement is the element to be entered
1294  }
1295  else
1296  {
1298  // use LeadElement for an exit continuation
1299  }
1300  }
1301  }
1302  }
1303  }
1304  if(Straddle == LeadMidLag) //train on a half element
1305  {
1307  {
1308  Utilities->CallLogPop(654);
1309  return;
1310  }
1311  }
1312  else //train fully on 2 elements
1313  {
1315  {
1316  Utilities->CallLogPop(655);
1317  return;
1318  }
1319  }
1320  if((LeadElement > -1) && (MidElement > -1))
1321  {
1323  {
1324  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1325  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1326  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1327  SignallerStoppingFlag = false;
1328  StepForwardFlag = false;
1329  }
1330  }
1331  if(Stopped())
1332  // this is what prevents another movement if the train is stopped
1333  {
1334  if(TrainFailurePending) // ok, stopped so PlotElements set
1335  {
1336  TrainHasFailed(5);
1337  }
1338  BrakeRate = 0;
1339  Utilities->CallLogPop(656);
1340  return;
1341  }
1342  // here when ready for next move
1343 
1344  //added at v2.10.0 to set SPADFlag if red signal immediately ahead (as it will be if in a locked route)
1345  //check if due to run past a red signal & if so set SPADFlag (SetTrainMovementValues & its SPAD check only called when arrive fully on 2 elements)
1346  if(Straddle == LeadMid) //fully on 2 elements
1347  {
1348  if(LeadElement > -1)
1349  {
1350  if(Track->TrackElementAt(1402, LeadElement).Conn[LeadExitPos] > -1)
1351  {
1353  if(TIF.TrackType == SignalPost)
1354  {
1355  int TIFEntryPos = Track->TrackElementAt(1405, LeadElement).ConnLinkPos[LeadExitPos];
1356  int TIFExitPos = 0;
1357  if(TIFEntryPos == 0)
1358  {
1359  TIFExitPos = 1;
1360  }
1361  if((TIF.Config[TIFExitPos] == Signal) && TIF.Attribute == 0 && (ExitSpeedHalf > 1) && !AllowedToPassRedSignal) //use ExitSpeedHalf as may have been stopped at signal so entryspeed is 0
1362  {
1363  SPADFlag = true; // user has to intervene to reset & restart after spad
1364  }
1365  }
1366  }
1367  }
1368  }
1369 
1370  // check for train in front & if so stop at next access (when train fully on element next to train)
1371  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1372  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1373  // variable TrainInFrontInSignallerModeFlag
1374  {
1375  if(LeadElement > -1)
1376  {
1377  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1378  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1379  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1380  // true if another train on NextEntryPos track whether bridge or not
1381  {
1382  StoppedForTrainInFront = true;
1383  }
1384  else
1385  {
1386  StoppedForTrainInFront = false;
1387  }
1388  }
1389  }
1390  if((Straddle == LeadMid) && SPADFlag)
1391  // give message + plot background when ready to move half past the signal
1392  {
1393  if(NextElementPosition > -1)
1394  {
1395  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1396  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1397  {
1398  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1400  // if goes past 2 signals then give message twice
1402  }
1403  }
1404  }
1405  if(Straddle == LeadMidLag)
1406  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1407  {
1408  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1409  if(SPADFlag)
1410  {
1411  if(ExitSpeedFull == 0)
1412  {
1413  StoppedAfterSPAD = true;
1414  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1415  }
1416  }
1418  {
1419  if(ExitSpeedFull == 0)
1420  {
1421  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1422  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1423  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1424  // is sent at the right time and once only.
1425  SignallerStopped = true;
1426  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1427  StepForwardFlag = false;
1428  SignallerStoppingFlag = false;
1429  TTrackElement TE;
1430  AnsiString Loc = "";
1431  bool LocNamed = false;
1432  if(LeadElement > -1)
1433  {
1434  TE = Track->TrackElementAt(782, LeadElement);
1435  if(TE.ActiveTrackElementName != "")
1436  {
1437  Loc = TE.ActiveTrackElementName;
1438  LocNamed = true;
1439  }
1440  else
1441  {
1442  Loc = "track element " + TE.ElementID;
1443  }
1444  }
1445  if((MidElement > -1) && !LocNamed)
1446  {
1447  TE = Track->TrackElementAt(783, MidElement);
1448  if(TE.ActiveTrackElementName != "")
1449  {
1450  Loc = TE.ActiveTrackElementName;
1451  LocNamed = true;
1452  }
1453  else if(Loc == "")
1454  {
1455  Loc = "track element " + TE.ElementID;
1456  }
1457  }
1458  if(Loc == "")
1459  {
1460  Loc = "outside railway";
1461  // must have stopped after left at a continuation (because both lead & mid == -1)
1462  }
1463  else
1464  {
1465  Loc = "at " + Loc;
1466  }
1467  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1468  }
1469  }
1470  if(LeadElement > -1) // if an exit continuation then not set
1471  {
1472  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1473  {
1475  }
1476  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1477  {
1478  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1479  {
1480  LeadExitPos = 1;
1481  }
1482  else
1483  {
1484  LeadExitPos = 3;
1485  }
1486  }
1487  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1488  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1489  }
1490  else
1491  {
1492  NextElementPosition = -1;
1493  NextEntryPos = -1;
1494  }
1497  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1498 
1499  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1500  {
1501  StoppedWithoutPower = true;
1502  }
1503  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1504  // may be buffers or continuation. SPADFlag added at v2.1.0
1505  // so don't override the SPAD colour & don't set StoppedAtSignal
1506  {
1507  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1508  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !StoppedAtLocation)
1509  {
1510  StoppedAtSignal = true;
1511  if(!StoppedWithoutPower)
1512  // leave background as is if no power, but set StoppedAtSignal
1513  {
1515  }
1516  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1517  }
1518  }
1519  if(!Stopped())
1520  {
1521  if((NextElementPosition > -1) && (NextEntryPos > -1))
1522  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1523  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1524  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1525  // function for fuller explanation
1526  {
1527  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1528  // NextElement is the element to be entered
1529  }
1530  // follow the continuation exits:-
1531  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1532  {
1534  // Use LeadElement for calcs if lead is a continuation
1535  }
1536  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1537  {
1539  // Use MidElement for calcs if mid is a continuation
1540  }
1541  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1542  {
1544  // Use LagElement for calcs if lag is a continuation
1545  }
1546  }
1547  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1548  if(AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1549  // Trains may not be in a route
1550  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1551  {
1552  // NB if LeadElement == -1 then the above test returns false
1553  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1554  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1555  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1556  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1557  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1558  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1559  FirstPair.second).GetELink() == TempELink))
1560  {
1561  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1562  }
1563  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1564  SecondPair.second).GetELink() == TempELink))
1565  {
1566  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1567  }
1568  }
1569  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1570  // Trains may not be in a route
1571  {
1572  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1573  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1574  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1575  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1576  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1577  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1578  FirstPair.second).GetELink() == TempELink))
1579  {
1580  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1581  }
1582  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1583  SecondPair.second).GetELink() == TempELink))
1584  {
1585  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1586  }
1587  }
1588  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1589  // Trains may not be in a route
1590  {
1591  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1592  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1593  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1594  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1595  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1596  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1597  FirstPair.second).GetELink() == TempELink))
1598  {
1599  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1600  }
1601  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1602  SecondPair.second).GetELink() == TempELink))
1603  {
1604  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1605  }
1606  AllRoutes->CheckMapAndRoutes(8); // test
1607  }
1608  if(LagElement > -1)
1609  // not entering at a continuation so can deal with train leaving the lag element
1610  {
1612  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1613  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1614  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1615 
1616  TPrefDirElement PrefDirElement;
1617  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1618  // as this is a 16x16 graphic
1620  {
1622  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1623  }
1624  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1625  {
1626  int RouteNumber;
1627  TrainGone = true;
1628  // flag to indicate train to be deleted - outside this function
1630  {
1631  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1632  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1633  // calc distance from & inc last signal to exit
1634  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1635  int NewLastElement = 0, NewLastExitPos = 0;
1636  // need above because can't change LastElement & LastExitPos until both new values obtained
1637  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1638  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1639  LastElement).TrackType != Points))
1640  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1641  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1642  // leave CumDistance as it was in these circumstances.
1643  {
1644  if(LastExitPos < 2)
1645  {
1646  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1647  }
1648  else
1649  {
1650  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1651  }
1652  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1653  if(NewLastElement == -1)
1654  // this will catch buffers or any other connection failure
1655  {
1656  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1657  }
1658  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1659  if(NewLastExitPos == -1)
1660  {
1661  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1662  }
1663  LastElement = NewLastElement;
1664  LastExitPos = NewLastExitPos;
1665  }
1666  // if at signal add this in too
1667  if(CumDistance < 1200)
1668  {
1669  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1670  }
1671  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1672  // else use 1200m - CumDistance
1673  int FirstDistance = 0;
1674  if(CumDistance >= 1200)
1675  {
1676  FirstDistance = 100;
1677  }
1678  else
1679  {
1680  FirstDistance = 1200 - CumDistance;
1681  }
1682  if(FirstDistance < 100)
1683  {
1684  FirstDistance = 100; // don't allow < 100
1685  }
1686  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1687  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1688  if(ExitSpeedFull > 20.0)
1689  {
1690  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1691  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1692  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1693  // 4320.0 = 3.6 * 1200, .0 to make it a double
1694  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1695  }
1696  else
1697  {
1698  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1699  ContinuationAutoSigEntry.SecondDelay = 120.0;
1700  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1701  }
1702  ContinuationAutoSigEntry.AccessNumber = 0;
1703  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1705  {
1707  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1708  VectorIT++)
1709  {
1710  if(VectorIT->RouteNumber == RouteNumber)
1711  {
1712  // another train has passed out of same route so erase earlier entry
1713  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1714  break;
1715  }
1716  }
1717  }
1718  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1719  }
1721  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1722  Display->Update();
1723  // need to keep this since Update() not called for PlotSmallOutput as too slow
1724  Utilities->CallLogPop(659);
1725  return;
1726  }
1727  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1728  if(LeadElement > -1)
1729  {
1731  // changed to lead so reset early
1732  {
1733  Track->TrackElementAt(225, LeadElement).Attribute = 0; // red
1735  // don't plot if zoomed out
1736  if(!Display->ZoomOutFlag)
1737  {
1739  }
1740  // covers signal resetting in same direction
1741  }
1742  }
1744  {
1745  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1746  {
1747  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1748  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1749  TPrefDirElement PrefDirElement;
1750  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1752  {
1754  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1755  }
1757  LockedVectorNumber)))
1758  {
1760  }
1761  }
1762  }
1763  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1764  {
1765  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1767  // don't plot if zoomed out
1768  if(!Display->ZoomOutFlag)
1769  {
1771  }
1772  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1773  }
1775  {
1776  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1777  {
1778  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1779  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1780  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1781  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1782  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1783  int RouteNumber;
1785  // already know it's an autosigsroute, this is just to get the RouteNumber
1786  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1787  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1788  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1789  int RouteNumber2;
1791  // already know it's an autosigsroute, this is just to get the RouteNumber
1792  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1793  // note that if not in a route (as likely) then RouteNumber2 set to -1 )
1794  {
1795  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1796  // this was in the 1.3.0 addition but without the condition
1797  }
1798  // end of 1.3.2 addition
1799  // end of 1.3.0.addition
1800  }
1801  TPrefDirElement PrefDirElement;
1802  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
1804  {
1806  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1807  }
1808  }
1809  }
1810  }
1811  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
1812  if(Straddle == LeadMid)
1813  {
1814  AllowedToPassRedSignal = false;
1815  // if had been allowed to pass then at this point it will move half onto signal so can be reset
1816  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
1817  if(DerailPending)
1818  // set during last GetLeadElement, but only act on it when train fully on offending point
1819  // i.e. next time Straddle reaches LeadMid
1820  {
1821  Derailed = true;
1822  DerailPending = false;
1826  Utilities->CallLogPop(657);
1827  return;
1828  }
1835  Straddle = MidLag;
1836  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
1837  // LeadElement during this function (note that if stopped at signal then won't get this far)
1838  if(LeadElement > -1)
1839  {
1841  // i.e an exit continuation only
1842  // if don't exclude entry continuations then can't progress past it
1843  {
1844  LeadElement = -1;
1845  }
1846  else
1847  {
1848  GetLeadElement(0);
1849  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
1851  if(Stopped())
1852  {
1853  if(TrainFailurePending) // ok, stopped so PlotElements set
1854  {
1855  TrainHasFailed(6);
1856  }
1857  Utilities->CallLogPop(658);
1858  return; // i.e. don't move forward one step if next element is a red signal
1859  }
1860  }
1861  }
1862  }
1863  if(LagElement > -1)
1864  {
1865  // below are the actions required at both half moves for LagElement > -1
1867 
1868  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
1869  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
1870  // need to do this for each half element
1871 
1872  TPrefDirElement PrefDirElement;
1873  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
1874  {
1875  int RouteNumber; // holder for call below - not used
1877  {
1878  if(Utilities->clTransparent == TColor(0xFFFFFF))
1879  // change to black for a white background
1880  {
1882  // only applies for AutoSigs Route in case was locked & timed out
1883  }
1884  else
1885  // change to white for a dark background
1886  {
1888  // only applies for AutoSigs Route in case was locked & timed out
1889  }
1891  }
1892  }
1894  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
1895  // or a train on the opposite track - needs to be replotted
1896  }
1897  // update all array values
1898  HOffset[3] = HOffset[2];
1899  HOffset[2] = HOffset[1];
1900  HOffset[1] = HOffset[0];
1901  VOffset[3] = VOffset[2];
1902  VOffset[2] = VOffset[1];
1903  VOffset[1] = VOffset[0];
1904  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
1905 
1906  BackgroundPtr[3] = BackgroundPtr[2];
1907  BackgroundPtr[2] = BackgroundPtr[1];
1908  BackgroundPtr[1] = BackgroundPtr[0];
1909  BackgroundPtr[0] = TempPtr;
1910 
1911  // update headcode graphics depending on Lead entry value
1912  if(LeadElement > -1) // if Lead is -1 then stays as is
1913  {
1915  {
1916  for(int x = 0; x < 4; x++)
1917  {
1918  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
1919  }
1920  }
1921  else
1922  {
1923  for(int x = 0; x < 4; x++)
1924  {
1926  }
1927  }
1928  }
1929  if(TrainMode == Timetable)
1930  {
1932  }
1933  else
1934  {
1936  }
1938 
1939  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
1940  if(LeadElement > -1)
1941  {
1942  if(Straddle == MidLag)
1943  // just about to move half onto the new lead element
1944  {
1946  // pick up new background bitmap [0]
1948  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
1949  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
1950  // check if own ID for entry at continuation, else crashes into itself!
1951  {
1952  // OK if crossing on a bridge
1953  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
1954  if(OtherTrainEntryPos == -1)
1955  {
1956  throw Exception("Error - OtherTrainEntryPos not set");
1957  }
1958  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
1959  // LeadEntryPos for rear end crashes
1960  (LeadExitPos == OtherTrainEntryPos))
1961  // LeadExitPos for head-on crashes
1962  {
1964  Crashed = true; // only set if Straddle = MidLag
1965  CallingOnFlag = false;
1966  // in case was set, need to disable call on if call on button had been pressed
1967  }
1968  }
1969  else if(MidElement > -1) // will be -1 for continuation entries
1970  {
1971  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
1972  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
1973  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
1974  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
1975  int OtherTrainID = -1;
1976  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
1977  {
1978  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
1979  {
1980  TrainCrashedInto = OtherTrainID;
1981  Crashed = true; // only set if Straddle = MidLag
1982  CallingOnFlag = false;
1983  // in case was set, need to disable call on if call on button had been pressed
1984  }
1985  }
1986  }
1987  }
1988  else
1989  {
1991  // pick up new background bitmap [0]
1993  }
1994  PlotElement[0] = LeadElement;
1996  PlotTrainGraphic(12, 0, Display);
1997  }
1998  if(MidElement > -1)
1999  {
2000  PlotElement[2] = MidElement;
2002  PlotTrainGraphic(1, 2, Display);
2003  }
2004  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
2005  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
2006  if(Straddle == MidLag)
2007  {
2008  if(MidElement > -1)
2009  {
2010  PlotElement[1] = MidElement;
2012  PlotTrainGraphic(2, 1, Display);
2013  }
2014  if(LagElement > -1)
2015  {
2016  PlotElement[3] = LagElement;
2018  PlotTrainGraphic(3, 3, Display);
2019  }
2020  }
2021  else // Straddle == LeadMidLag
2022  {
2023  if(LeadElement > -1)
2024  {
2025  PlotElement[1] = LeadElement;
2027  PlotTrainGraphic(4, 1, Display);
2028  }
2029  if(MidElement > -1)
2030  {
2031  PlotElement[3] = MidElement;
2033  PlotTrainGraphic(5, 3, Display);
2034  }
2035  }
2036  if(Crashed)
2037  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
2038  {
2043  // in case was set, need to disable call on if call on button had been pressed
2050  Straddle = LeadMidLag;
2051  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
2052  Display->Update();
2053  // resurrected when Update() dropped from PlotOutput etc
2054  Utilities->CallLogPop(660);
2055  return;
2056  }
2057  // deal here with station stops & pass times after all replotting done but before Straddle changed
2058  if(TrainMode == Timetable)
2059  {
2060  if(Straddle == LeadMidLag)
2061  {
2062  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2063  {
2064  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2065  // to point to the location arrival entry - before a change of direction
2066  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2067  bool StopRequired = false;
2068  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
2069  if(TTVPos > -1) // -1 if can't find it or if name is ""
2070  {
2071  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2072  // or a station where next element contains a train or a stop signal, if so
2073  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2074  // to test the actual track the train is on since it can't be a platform
2075  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2076  TTrackElement NextTrackElement; // default for now
2077  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2078  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2079  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2080  int NextElementEntryPos = -1;
2081  int NextElementExitPos = -1;
2082  bool TrainOnNextElement = false;
2083  bool StopSignalAtNextElement = false;
2084  if(ForwardConnection)
2085  // if no forward connection can't derive anything from it without errors
2086  {
2087  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2088  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2089  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2090  // this is only for signals so no need to worry about points ambiguity
2091  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2092  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2093  }
2094  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2095  {
2096  if(TTVPos > 0)
2097  {
2099  ActionVectorEntryPtr += TTVPos;
2100  }
2101  if(StopRequired)
2102  {
2103  StoppedAtLocation = true;
2104  StoppedAtSignal = false;
2105  // may have been set earlier at line 925 so need to reset as
2106  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2107  // in zoom out mode
2108  if(!TrainFailed)
2109  {
2111  // pale green
2112  }
2115  {
2116  TimeTimeLocArrived = true;
2117  // used in case of later signaller control, when need to know
2118  // whether had arrived or not, to avoid sending the arrival
2119  // message twice, see TInterface::TimetableControl1Click
2120  }
2121  }
2122  else
2123  {
2125  }
2127  {
2129  }
2130  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2132  }
2133  }
2134  }
2135  }
2136  }
2137  if(Straddle == MidLag)
2138  {
2139  Straddle = LeadMidLag;
2140  FirstHalfMove = false;
2141  }
2142  else if(Straddle == LeadMidLag)
2143  {
2144  Straddle = LeadMid;
2145  FirstHalfMove = true;
2146  }
2147  else if(Straddle == LeadMid)
2148  {
2149  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2150  }
2151  if(TrainFailurePending) // ok, moving but PlotElements set above
2152  {
2153  TrainHasFailed(7);
2154  }
2155  Display->Update();
2156  // need to keep this since Update() not called for PlotSmallOutput as too slow
2157  Utilities->CallLogPop(661);
2158 }
2159 
2160 // ----------------------------------------------------------------------------
2161 
2162 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2163 {
2164  switch(CodeChar)
2165  {
2166  case '0':
2167  return(RailGraphics->Code0);
2168 
2169  case '1':
2170  return(RailGraphics->Code1);
2171 
2172  case '2':
2173  return(RailGraphics->Code2);
2174 
2175  case '3':
2176  return(RailGraphics->Code3);
2177 
2178  case '4':
2179  return(RailGraphics->Code4);
2180 
2181  case '5':
2182  return(RailGraphics->Code5);
2183 
2184  case '6':
2185  return(RailGraphics->Code6);
2186 
2187  case '7':
2188  return(RailGraphics->Code7);
2189 
2190  case '8':
2191  return(RailGraphics->Code8);
2192 
2193  case '9':
2194  return(RailGraphics->Code9);
2195 
2196  case 'A':
2197  return(RailGraphics->CodeA);
2198 
2199  case 'B':
2200  return(RailGraphics->CodeB);
2201 
2202  case 'C':
2203  return(RailGraphics->CodeC);
2204 
2205  case 'D':
2206  return(RailGraphics->CodeD);
2207 
2208  case 'E':
2209  return(RailGraphics->CodeE);
2210 
2211  case 'F':
2212  return(RailGraphics->CodeF);
2213 
2214  case 'G':
2215  return(RailGraphics->CodeG);
2216 
2217  case 'H':
2218  return(RailGraphics->CodeH);
2219 
2220  case 'I':
2221  return(RailGraphics->CodeI);
2222 
2223  case 'J':
2224  return(RailGraphics->CodeJ);
2225 
2226  case 'K':
2227  return(RailGraphics->CodeK);
2228 
2229  case 'L':
2230  return(RailGraphics->CodeL);
2231 
2232  case 'M':
2233  return(RailGraphics->CodeM);
2234 
2235  case 'N':
2236  return(RailGraphics->CodeN);
2237 
2238  case 'O':
2239  return(RailGraphics->CodeO);
2240 
2241  case 'P':
2242  return(RailGraphics->CodeP);
2243 
2244  case 'Q':
2245  return(RailGraphics->CodeQ);
2246 
2247  case 'R':
2248  return(RailGraphics->CodeR);
2249 
2250  case 'S':
2251  return(RailGraphics->CodeS);
2252 
2253  case 'T':
2254  return(RailGraphics->CodeT);
2255 
2256  case 'U':
2257  return(RailGraphics->CodeU);
2258 
2259  case 'V':
2260  return(RailGraphics->CodeV);
2261 
2262  case 'W':
2263  return(RailGraphics->CodeW);
2264 
2265  case 'X':
2266  return(RailGraphics->CodeX);
2267 
2268  case 'Y':
2269  return(RailGraphics->CodeY);
2270 
2271  case 'Z':
2272  return(RailGraphics->CodeZ);
2273 
2274  case 'a':
2275  return(RailGraphics->Code_a);
2276 
2277  case 'b':
2278  return(RailGraphics->Code_b);
2279 
2280  case 'c':
2281  return(RailGraphics->Code_c);
2282 
2283  case 'd':
2284  return(RailGraphics->Code_d);
2285 
2286  case 'e':
2287  return(RailGraphics->Code_e);
2288 
2289  case 'f':
2290  return(RailGraphics->Code_f);
2291 
2292  case 'g':
2293  return(RailGraphics->Code_g);
2294 
2295  case 'h':
2296  return(RailGraphics->Code_h);
2297 
2298  case 'i':
2299  return(RailGraphics->Code_i);
2300 
2301  case 'j':
2302  return(RailGraphics->Code_j);
2303 
2304  case 'k':
2305  return(RailGraphics->Code_k);
2306 
2307  case 'l':
2308  return(RailGraphics->Code_l);
2309 
2310  case 'm':
2311  return(RailGraphics->Code_m);
2312 
2313  case 'n':
2314  return(RailGraphics->Code_n);
2315 
2316  case 'o':
2317  return(RailGraphics->Code_o);
2318 
2319  case 'p':
2320  return(RailGraphics->Code_p);
2321 
2322  case 'q':
2323  return(RailGraphics->Code_q);
2324 
2325  case 'r':
2326  return(RailGraphics->Code_r);
2327 
2328  case 's':
2329  return(RailGraphics->Code_s);
2330 
2331  case 't':
2332  return(RailGraphics->Code_t);
2333 
2334  case 'u':
2335  return(RailGraphics->Code_u);
2336 
2337  case 'v':
2338  return(RailGraphics->Code_v);
2339 
2340  case 'w':
2341  return(RailGraphics->Code_w);
2342 
2343  case 'x':
2344  return(RailGraphics->Code_x);
2345 
2346  case 'y':
2347  return(RailGraphics->Code_y);
2348 
2349  case 'z':
2350  return(RailGraphics->Code_z);
2351 
2352  default:
2353  return(RailGraphics->TempHeadCode);
2354  }
2355 }
2356 
2357 // ----------------------------------------------------------------------------
2358 
2359 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2360 {
2361  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2362  if(Code.Length() != 4)
2363  {
2364  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2365  }
2366  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2367  {
2368  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2369  }
2370  if(BackgroundColour != clB5G5R5)
2371  // i.e. not the basic graphic colour as loaded from resource file
2372  {
2373  for(int x = 0; x < 4; x++)
2374  {
2376  }
2377  }
2378  Utilities->CallLogPop(1484);
2379 }
2380 
2381 // ----------------------------------------------------------------------------
2382 
2383 void TTrain::GetLeadElement(int Caller)
2384 // assumes Mid & Lag already set, sets LeadElement,
2385 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2386 {
2387  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2388  DerailPending = false;
2392  {
2393  // attr 0=straight, - links 0 & 1 (0 = lead)
2394  // attr 1=diverging, - links 2 & 3 (2 = lead)
2395  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2396  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2397 
2398  // if enter at lead, exit at whatever attr set at
2399  // if enter at lag, exit at lead, but set derail wrt attribute
2400  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2401  {
2402  LeadExitPos = 1;
2403  }
2404 
2405  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2406  // best to be on safe side
2407  else if(LeadEntryPos == 0)
2408  {
2409  LeadEntryPos = 2;
2410  LeadExitPos = 3;
2411  }
2412  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2413  {
2414  LeadEntryPos = 0;
2415  LeadExitPos = 1;
2416  }
2417  else if(LeadEntryPos == 2)
2418  {
2419  LeadExitPos = 3;
2420  }
2421 
2422  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2423  {
2424  LeadExitPos = 0;
2425  }
2426  else if(LeadEntryPos == 1)
2427  {
2428  LeadExitPos = 0;
2429  DerailPending = true;
2430  }
2431  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2432  {
2433  LeadExitPos = 0;
2434  DerailPending = true;
2435  }
2436  else if(LeadEntryPos == 3)
2437  {
2438  LeadExitPos = 0;
2439  }
2440  }
2441  else if(LeadEntryPos == 0)
2442  {
2443  LeadExitPos = 1;
2444  }
2445  else if(LeadEntryPos == 1)
2446  {
2447  LeadExitPos = 0;
2448  }
2449  else if(LeadEntryPos == 2)
2450  {
2451  LeadExitPos = 3;
2452  }
2453  else if(LeadEntryPos == 3)
2454  {
2455  LeadExitPos = 2;
2456  }
2457  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2458 /* signal check moved to Update() function
2459  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2460  && (TrackElement.Attribute == 0))//0 = red
2461  {
2462  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2463  }
2464  else
2465  {
2466  StoppedAtSignal = false;
2467  }
2468 */
2469  Utilities->CallLogPop(662);
2470 }
2471 
2472 // ----------------------------------------------------------------------------
2473 
2474 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2475 {
2476  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2477  switch(Link)
2478  {
2479  case 1:
2480  {
2481  HOffset = 0;
2482  VOffset = 0;
2483  break;
2484  }
2485 
2486  case 2:
2487  {
2488  HOffset = 4;
2489  VOffset = 0;
2490  break;
2491  }
2492 
2493  case 3:
2494  {
2495  HOffset = 8;
2496  VOffset = 0;
2497  break;
2498  }
2499 
2500  case 4:
2501  {
2502  HOffset = 0;
2503  VOffset = 4;
2504  break;
2505  }
2506 
2507  case 6:
2508  {
2509  HOffset = 8;
2510  VOffset = 4;
2511  break;
2512  }
2513 
2514  case 7:
2515  {
2516  HOffset = 0;
2517  VOffset = 8;
2518  break;
2519  }
2520 
2521  case 8:
2522  {
2523  HOffset = 4;
2524  VOffset = 8;
2525  break;
2526  }
2527 
2528  case 9:
2529  {
2530  HOffset = 8;
2531  VOffset = 8;
2532  break;
2533  }
2534 
2535  default:
2536  {
2537  throw Exception("Error in GetOffsetValues - Link value wrong");
2538  }
2539  }
2540  Utilities->CallLogPop(674);
2541 }
2542 
2543 // ---------------------------------------------------------------------------
2544 
2545 bool TTrain::LowEntryValue(int EntryLink) const
2546 {
2547 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2548  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2549 */
2550  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2551  {
2552  return(true);
2553  }
2554  else
2555  {
2556  return(false);
2557  }
2558 }
2559 
2560 // ---------------------------------------------------------------------------
2561 
2562 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2563 {
2564  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2565  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2566  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2567  // default values
2568  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2569 
2570  TAllRoutes::TRouteType RouteType;
2571 
2572  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2573 
2574  TRect SourceRect, DestRect;
2575 
2576  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2577  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2578  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2579  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2580 
2581  TempGraphic->PixelFormat = pf8bit;
2582  TempGraphic->Width = 16;
2583  TempGraphic->Height = 16;
2584  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2585 
2586  if(TempElement.TrackType == Points)
2587  {
2588  TempGraphic->Assign(TempElement.GraphicPtr);
2589  TempGraphic->Transparent = true;
2590  TempGraphic->TransparentColor = Utilities->clTransparent;
2591  if(RouteType == TAllRoutes::AutoSigsRoute)
2592  {
2593  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2594  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2595  }
2596  else
2597  {
2598  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2599  }
2600  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2601  }
2602  else if(TempElement.TrackType == GapJump) // plot set gap
2603  {
2604  if(TempElement.SpeedTag == 88)
2605  {
2606  TempGraphic->Assign(RailGraphics->gl88set);
2607  }
2608  else if(TempElement.SpeedTag == 89)
2609  {
2610  TempGraphic->Assign(RailGraphics->gl89set);
2611  }
2612  else if(TempElement.SpeedTag == 90)
2613  {
2614  TempGraphic->Assign(RailGraphics->gl90set);
2615  }
2616  else if(TempElement.SpeedTag == 91)
2617  {
2618  TempGraphic->Assign(RailGraphics->gl91set);
2619  }
2620  else if(TempElement.SpeedTag == 92)
2621  {
2622  TempGraphic->Assign(RailGraphics->gl92set);
2623  }
2624  else if(TempElement.SpeedTag == 93)
2625  {
2626  TempGraphic->Assign(RailGraphics->bm93set);
2627  }
2628  else if(TempElement.SpeedTag == 94)
2629  {
2630  TempGraphic->Assign(RailGraphics->bm94set);
2631  }
2632  else if(TempElement.SpeedTag == 95)
2633  {
2634  TempGraphic->Assign(RailGraphics->gl95set);
2635  }
2636  TempGraphic->Transparent = true;
2637  TempGraphic->TransparentColor = Utilities->clTransparent;
2638  if(RouteType == TAllRoutes::AutoSigsRoute)
2639  {
2640  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2641  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2642  }
2643  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2644  }
2645  // new for version 0.6
2646  else if(TempElement.TrackType == SignalPost)
2647  {
2648  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2649  {
2650  for(int x = 0; x < 40; x++)
2651  {
2652  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2653  // need to stop aspect
2654  {
2655  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2656  break;
2657  }
2658  }
2659  }
2660  else // normal signal
2661  {
2662  TempGraphic->Assign(TempElement.GraphicPtr);
2663  // GraphicPtr set to normal signal in a signal track element
2664  }
2665  TempGraphic->Transparent = true;
2666  TempGraphic->TransparentColor = Utilities->clTransparent;
2667  if(RouteType == TAllRoutes::AutoSigsRoute)
2668  {
2669  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2670  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2671  }
2672  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2673  }
2674  else
2675  {
2676  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2677  // can't name points gaps or signals so 'else' OK
2678  bool FoundFlag;
2679  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2680  if(FoundFlag)
2681  {
2683  {
2684  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2685  TempGraphic->Assign(RailGraphics->bmName);
2686  TempGraphic->Transparent = true;
2687  TempGraphic->TransparentColor = Utilities->clTransparent;
2688  if(RouteType == TAllRoutes::AutoSigsRoute)
2689  {
2690  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2691  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2692  }
2693  else
2694  {
2695  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2696  }
2697  // draw track on top
2698  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2699  }
2700  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2701  {
2702  TempGraphic->Assign(TempElement.GraphicPtr);
2703  TempGraphic->Transparent = true;
2704  TempGraphic->TransparentColor = Utilities->clTransparent;
2705  // note that can't be an AutoSigsRoute
2706  // now overlay the LC central portion
2707  int BDVectorPos = -1; //not used
2708  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2709  {
2710  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2711  }
2712  else
2713  {
2714  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2715  }
2716  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2717  }
2718  else
2719  {
2720  TempGraphic->Assign(TempElement.GraphicPtr);
2721  TempGraphic->Transparent = true;
2722  TempGraphic->TransparentColor = Utilities->clTransparent;
2723  if(RouteType == TAllRoutes::AutoSigsRoute)
2724  {
2725  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2726  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2727  }
2728  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2729  }
2730  }
2731  else
2732  {
2733  TempGraphic->Assign(TempElement.GraphicPtr);
2734  TempGraphic->Transparent = true;
2735  TempGraphic->TransparentColor = Utilities->clTransparent;
2736  if(RouteType == TAllRoutes::AutoSigsRoute)
2737  {
2738  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2739  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2740  }
2741  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2742  }
2743  }
2744  delete TempGraphic;
2745  Utilities->CallLogPop(675);
2746 }
2747 
2748 // ---------------------------------------------------------------------------
2749 
2750 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2751 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2752 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2753 /*
2754 
2755  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2756  {
2757  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2758  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2759  TAllRoutes::TRouteType RouteType;
2760  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2761  // default values
2762  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2763  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2764  TRect SourceRect, DestRect, ScreenSourceRect;
2765  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2766 
2767  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2768  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2769  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2770 
2771  //add text & user graphics if any to *GraphicPtr prior to adding the track
2772  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2773  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2774  int Right = Left + 8;
2775  int Bottom = Top + 8;
2776  ScreenSourceRect.init(Left, Top, Right, Bottom);
2777  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2778  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2779 
2780  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2781  TempGraphic->PixelFormat = pf8bit;
2782  TempGraphic->Width = 16;
2783  TempGraphic->Height = 16;
2784 
2785  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2786  SourceGraphic->PixelFormat = pf8bit;
2787  SourceGraphic->Width = 16;
2788  SourceGraphic->Height = 16;
2789  SourceGraphic->Transparent = true;
2790  SourceGraphic->TransparentColor = Utilities->clTransparent;
2791 
2792  if (TempElement.TrackType == Points)
2793  {
2794  TempGraphic->Assign(TempElement.GraphicPtr);
2795  TempGraphic->Transparent = true;
2796  TempGraphic->TransparentColor = Utilities->clTransparent;
2797  if (RouteType == TAllRoutes::AutoSigsRoute)
2798  {
2799  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2800  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2801  }
2802  else
2803  {
2804  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2805  }
2806  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2807  }
2808  else if (TempElement.TrackType == GapJump) // plot set gap
2809  {
2810  if (TempElement.SpeedTag == 88)
2811  TempGraphic->Assign(RailGraphics->gl88set);
2812  else if (TempElement.SpeedTag == 89)
2813  TempGraphic->Assign(RailGraphics->gl89set);
2814  else if (TempElement.SpeedTag == 90)
2815  TempGraphic->Assign(RailGraphics->gl90set);
2816  else if (TempElement.SpeedTag == 91)
2817  TempGraphic->Assign(RailGraphics->gl91set);
2818  else if (TempElement.SpeedTag == 92)
2819  TempGraphic->Assign(RailGraphics->gl92set);
2820  else if (TempElement.SpeedTag == 93)
2821  TempGraphic->Assign(RailGraphics->bm93set);
2822  else if (TempElement.SpeedTag == 94)
2823  TempGraphic->Assign(RailGraphics->bm94set);
2824  else if (TempElement.SpeedTag == 95)
2825  TempGraphic->Assign(RailGraphics->gl95set);
2826  TempGraphic->Transparent = true;
2827  TempGraphic->TransparentColor = Utilities->clTransparent;
2828  if (RouteType == TAllRoutes::AutoSigsRoute) {
2829  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2830  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2831  }
2832  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2833  }
2834  // new for version 0.6
2835  else if (TempElement.TrackType == SignalPost)
2836  {
2837  if (TempElement.SigAspect == TTrackElement::GroundSignal)
2838  {
2839  for (int x = 0; x < 40; x++)
2840  {
2841  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2842  // need to stop aspect
2843  {
2844  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2845  break;
2846  }
2847  }
2848  }
2849  else // normal signal
2850  {
2851  TempGraphic->Assign(TempElement.GraphicPtr);
2852  // GraphicPtr set to normal signal in a signal track element
2853  }
2854  TempGraphic->Transparent = true;
2855  TempGraphic->TransparentColor = Utilities->clTransparent;
2856  if (RouteType == TAllRoutes::AutoSigsRoute) {
2857  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2858  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2859  }
2860  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2861  }
2862  else {
2863  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2864  // can't name points gaps or signals so 'else' OK
2865  bool FoundFlag;
2866  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
2867  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2868  if (FoundFlag)
2869  {
2870  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
2871  {
2872  GraphicPtr->Canvas->CopyRect(DestRect,
2873  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
2874  TempGraphic->Assign(RailGraphics->bmName);
2875  TempGraphic->Transparent = true;
2876  TempGraphic->TransparentColor = Utilities->clTransparent;
2877  if (RouteType == TAllRoutes::AutoSigsRoute)
2878  {
2879  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2880  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2881  }
2882  else
2883  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2884  // draw track on top
2885  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2886  SourceRect);
2887  }
2888  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
2889  TempGraphic->Assign(TempElement.GraphicPtr);
2890  TempGraphic->Transparent = true;
2891  TempGraphic->TransparentColor = Utilities->clTransparent;
2892  // note that can't be an AutoSigsRoute
2893  // now overlay the LC central portion
2894  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2895  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2896  SourceRect);
2897  }
2898  else {
2899  TempGraphic->Assign(TempElement.GraphicPtr);
2900  TempGraphic->Transparent = true;
2901  TempGraphic->TransparentColor = Utilities->clTransparent;
2902  if (RouteType == TAllRoutes::AutoSigsRoute) {
2903  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2904  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2905  }
2906  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2907  SourceRect);
2908  }
2909  }
2910  else {
2911  TempGraphic->Assign(TempElement.GraphicPtr);
2912  TempGraphic->Transparent = true;
2913  TempGraphic->TransparentColor = Utilities->clTransparent;
2914  if (RouteType == TAllRoutes::AutoSigsRoute) {
2915  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2916  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2917  }
2918  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2919  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
2920  }
2921  }
2922  delete TempGraphic;
2923  delete SourceGraphic;
2924  Utilities->CallLogPop();
2925  }
2926 */
2927 // ---------------------------------------------------------------------------
2928 
2929 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
2930 {
2931  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
2932  if(PlotElement[ArrayNumber] == -1)
2933  {
2934  Utilities->CallLogPop(676);
2935  return; // not plotted yet
2936  }
2937  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
2938  // set before plot so gap flashing stops first
2939  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2940  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
2941  // Only need to set ID for leading element, stays set until train finally leaves the element
2942  Plotted = true;
2943  Utilities->CallLogPop(677);
2944 }
2945 
2946 // ---------------------------------------------------------------------------
2947 
2948 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
2949 {
2950  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2951  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
2952 }
2953 
2954 // ---------------------------------------------------------------------------
2955 
2956 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
2957 {
2958  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
2959  HeadCode);
2960  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
2961  {
2962  Utilities->CallLogPop(678);
2963  return(true);
2964  }
2965  else
2966  {
2967  Utilities->CallLogPop(679);
2968  return(false);
2969  }
2970 }
2971 
2972 // ---------------------------------------------------------------------------
2973 
2974 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
2975 {
2976  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
2977  "," + HeadCode);
2978  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
2979  {
2980  Utilities->CallLogPop(680);
2981  return(true);
2982  }
2983  else
2984  {
2985  Utilities->CallLogPop(681);
2986  return(false);
2987  }
2988 }
2989 
2990 // ---------------------------------------------------------------------------
2991 
2992 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
2993 // test whether this train on a bridge on trackpos 0 & 1
2994 {
2995  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
2996  HeadCode);
2997  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
2998  {
2999  Utilities->CallLogPop(682);
3000  return(false);
3001  }
3002  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3003  if(Track->TrackElementAt(305, TrackVectorPosition).TrainIDOnBridgeTrackPos01 == TrainID)
3004  {
3005  if(Track->TrackElementAt(306, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
3006  {
3007  throw Exception("Error, same train on two different bridge tracks");
3008  }
3009  else
3010  {
3011  Utilities->CallLogPop(684);
3012  return(true);
3013  }
3014  }
3015  else
3016  {
3017  Utilities->CallLogPop(685);
3018  return(false);
3019  }
3020 }
3021 
3022 // ---------------------------------------------------------------------------
3023 
3024 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
3025 // test whether this train on a bridge on trackpos 2 & 3
3026 {
3027  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
3028  HeadCode);
3029  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
3030  {
3031  Utilities->CallLogPop(686);
3032  return(false);
3033  }
3034  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3035  if(Track->TrackElementAt(309, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
3036  {
3037  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
3038  Utilities->CallLogPop(687);
3039  return(true);
3040  }
3041  else
3042  {
3043  Utilities->CallLogPop(688);
3044  return(false);
3045  }
3046 }
3047 
3048 // ---------------------------------------------------------------------------
3049 
3050 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3051 {
3052  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3053  AnsiString(EntryPos) + "," + HeadCode);
3054  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
3055 
3056  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
3057  if(Track->GapFlashFlag)
3058  {
3060  {
3063  Track->GapFlashFlag = false;
3064  }
3065  }
3066  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3067  {
3068  if(EntryPos == -1)
3069  {
3070  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3071  }
3072  if(EntryPos < 2)
3073  {
3074  Track->TrackElementAt(312, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = TrainID;
3075  }
3076  else
3077  {
3078  Track->TrackElementAt(313, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = TrainID;
3079  }
3080  }
3081  Utilities->CallLogPop(690);
3082 }
3083 
3084 // ---------------------------------------------------------------------------
3085 
3086 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3087 {
3088  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3089  AnsiString(EntryPos) + "," + HeadCode);
3090  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3091  {
3092  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3093  }
3094  else
3095  {
3096  if(EntryPos == -1)
3097  {
3098  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3099  }
3100  if(EntryPos < 2)
3101  {
3102  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = -1;
3103  }
3104  else
3105  {
3106  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = -1;
3107  }
3108  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeTrackPos23 > -1))
3109  // i.e. other train on track 2&3
3110  {
3111  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeTrackPos23;
3112  }
3113  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeTrackPos01 > -1))
3114  // i.e. other train on track 1&2
3115  {
3116  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeTrackPos01;
3117  }
3118  else
3119  {
3120  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3121  }
3122  }
3123  Utilities->CallLogPop(691);
3124 }
3125 
3126 // ---------------------------------------------------------------------------
3127 
3128 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3129 {
3130  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3131  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3132  int LockedVectorNumber;
3133 
3134  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3135  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3136  {
3137  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3138  Utilities->CallLogPop(692);
3139  return;
3140  }
3141  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3142  // i.e other track is in a marked route
3143  // LinkPos doesn't have to be the entry position for the above check
3144  {
3145  TRect SourceRect, DestRect;
3146  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3147  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3148  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3149  // identify the route element for the other track
3150  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3151  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3152  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3153  int FirstELink, SecondELink = -1;
3154  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3155  // must be at least one
3156  if(RoutePair2.first > -1)
3157  {
3158  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3159  }
3160  TPrefDirElement RouteElement;
3161  // Graphics::TBitmap *RouteGraphic;
3162  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3163  // i.e. other track is in RoutePair2
3164  {
3165  if(SecondELink == -1)
3166  {
3167  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3168  }
3169  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3170  // error if both have same Link number
3171  {
3172  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3173  }
3174  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3175  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3176  }
3177  else // other track is in RoutePair1
3178  {
3179  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3180  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3181  }
3182  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3183  DestGraphic->PixelFormat = pf8bit;
3184  DestGraphic->Width = 8;
3185  DestGraphic->Height = 8;
3186  DestGraphic->Transparent = true;
3187  // has to be transparent or will overwrite the track that the train has just left
3188  DestGraphic->TransparentColor = Utilities->clTransparent;
3189  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3190  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3191  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3192  // plot locked route marker for other route if appropriate
3193  TPrefDirElement PrefDirElement; // holder for next call, unused
3194  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3195  if(StraddleValue == LeadMidLag)
3196  {
3198  PrefDirElement, LockedVectorNumber))
3199  {
3200  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3201  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3202  }
3203  }
3204  delete DestGraphic;
3205  }
3206  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3207  // also can only be a bridge or trains either have already or soon will crash
3208  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3209  {
3210  Utilities->CallLogPop(695);
3211  return;
3212  }
3213  if(ElementEntryPos > 1) // other train is on track 01
3214  {
3215  if(Track->TrackElementAt(336, ElementVecNum).TrainIDOnBridgeTrackPos01 > -1)
3216  {
3218  }
3219  }
3220  else // other train is on track 23
3221  {
3222  if(Track->TrackElementAt(338, ElementVecNum).TrainIDOnBridgeTrackPos23 > -1)
3223  {
3225  }
3226  }
3227  Utilities->CallLogPop(696);
3228 }
3229 
3230 // ---------------------------------------------------------------------------
3231 
3232 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3233 {
3234  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3235  AnsiString(EntryPos) + "," + HeadCode);
3236  int RouteNumber;
3237  bool WrongRoute = false;
3238  TPrefDirElement RouteElement;
3240  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3241 
3242  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3243  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3244  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3245  {
3246  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3247  {
3248  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3249  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3250  {
3251  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3252  {
3253  // don't call for stub end routes
3255  }
3256  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3257  Utilities->CallLogPop(697);
3258  return;
3259  }
3260  }
3261  // also need to check for a route on a crossing diagonal
3262  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3263  int LinkNumber = TrackElement.Link[EntryPos];
3264  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3265  {
3266  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3267  {
3268  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3269  bool LogActionErrorCalled = false;
3270  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3271  if(LinkNumber == 1)
3272  {
3273  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3274  {
3275  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3276  {
3277  // don't call for stub end routes
3279  LogActionErrorCalled = true;
3280  }
3281  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3282  }
3283  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3284  // not else in case have different routes on each diagonal, though shouldn't be possible
3285  {
3286  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3287  {
3288  // don't call for stub end routes
3290  }
3291  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3292  }
3293  }
3294 
3295  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3296  else if(LinkNumber == 3)
3297  {
3298  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3299  {
3300  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3301  {
3302  // don't call for stub end routes
3304  LogActionErrorCalled = true;
3305  }
3306  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3307  }
3308  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3309  // not else in case have different routes on each diagonal, though shouldn't be possible
3310  {
3311  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3312  {
3313  // don't call for stub end routes
3315  }
3316  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3317  }
3318  }
3319 
3320  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3321  else if(LinkNumber == 7)
3322  {
3323  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3324  {
3325  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3326  {
3327  // don't call for stub end routes
3329  LogActionErrorCalled = true;
3330  }
3331  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3332  }
3333  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3334  // not else in case have different routes on each diagonal, though shouldn't be possible
3335  {
3336  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3337  {
3338  // don't call for stub end routes
3340  }
3341  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3342  }
3343  }
3344 
3345  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3346  else if(LinkNumber == 9)
3347  {
3348  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3349  {
3350  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3351  {
3352  // don't call for stub end routes
3354  LogActionErrorCalled = true;
3355  }
3356  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3357  }
3358  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3359  // not else in case have different routes on each diagonal, though shouldn't be possible
3360  {
3361  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3362  {
3363  // don't call for stub end routes
3365  }
3366  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3367  }
3368  }
3369  }
3370  }
3371  Utilities->CallLogPop(698);
3372  return; // no route on other track or no other track
3373  }
3374  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3375  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3376  {
3377  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3378  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new at v2.4.2 for points check - Xeon repoted it 30/05/20. He found that for routes that
3379  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3380  {
3381  if(RouteElement.GetELinkPos() == EntryPos)
3382  {
3383  Utilities->CallLogPop(699);
3384  return; // right direction
3385  }
3386  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3387  {
3388  Utilities->CallLogPop(700);
3389  return; // right direction (points)
3390  }
3391  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3392  {
3393  Utilities->CallLogPop(701);
3394  return; // right direction (points)
3395  }
3396  else if(RouteElement.GetXLinkPos() == EntryPos)
3397  {
3398  WrongRoute = true;
3399  break; // wrong direction
3400  }
3401  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3402  {
3403  WrongRoute = true;
3404  break; // wrong direction
3405  }
3406  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3407  {
3408  WrongRoute = true;
3409  break; // wrong direction
3410  }
3411  }
3412  }
3413  if(!WrongRoute)
3414  {
3415  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3416  }
3417  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3418  {
3419  // don't call for stub end routes
3421  }
3422  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3423  Utilities->CallLogPop(703);
3424 }
3425 
3426 // ---------------------------------------------------------------------------
3427 
3428 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3429 {
3430  if(BackgroundColour == NewBackgroundColour)
3431  {
3432  return; // don't replot if already correct
3433 
3434  }
3435  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3436  bool ColourError = false, ColourError2 = false;
3437 
3438  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3439  if(ColourError)
3440  {
3441  ColourError2 = true;
3442  }
3443  for(int x = 0; x < 4; x++)
3444  {
3445  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3446  if(ColourError)
3447  {
3448  ColourError2 = true;
3449  }
3450  }
3451  if(ColourError2)
3452  {
3454  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3455  }
3456  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3457  // of motion
3458  for(int x = 0; x < 4; x++)
3459  {
3460  PlotTrainGraphic(6, x, Disp);
3461  }
3462  BackgroundColour = NewBackgroundColour;
3463  Display->Update();
3464  // need to keep this since Update() not called for PlotSmallOutput as too slow
3465  Utilities->CallLogPop(704);
3466 }
3467 
3468 // ---------------------------------------------------------------------------
3469 
3470 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3471 /*
3472 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3473 
3474 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3475 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3476 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3477 full-element moves.
3478 
3479 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3480 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3481 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3482 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3483 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3484 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3485 In this case set the brake rate to maximum to stop as soon as possible.
3486 
3487 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3488 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3489 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3490 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3491 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3492 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3493 
3494 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3495 first to see whether buffers or continuation) in turn is examined: first the length of the
3496 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3497 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3498 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3499 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3500 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3501 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3502 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3503 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3504 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3505 siding then again emeregency braking may be necessary and a crash may result.
3506 
3507 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3508 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3509 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3510 buffer, then the train accelerates for half the element and brakes for the other half.
3511 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3512 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3513 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3514 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3515 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3516 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3517 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3518 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3519 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3520 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3521 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3522 
3523 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3524 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3525 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3526 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3527 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3528 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3529 MaxBrakeRate.
3530 
3531 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3532 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3533 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3534 
3535 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3536 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3537 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3538 
3539 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3540 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3541 when Straddle == LeadMidLag
3542 */
3543 {
3544  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3545  AnsiString(EntryPos) + "," + HeadCode);
3546  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3547  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3548  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3549  TrainInFrontInSignallerModeFlag = false;
3550  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3551  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3552  bool SignallerStopRequired = false;
3553 
3555  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3556 
3557  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3558 
3559  OneLengthAccelDecel = false;
3560  BrakeRate = 0;
3561 
3562 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3563  if(CurrentTrackVectorPosition > -1)
3564  {
3565  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3566  {
3567  if((EntryPos == 0) || (EntryPos == 2))
3568  {
3569  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3570  {
3571  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3572  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3573  }
3574  else
3575  {
3576  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3577  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3578  }
3579  }
3580  else if(EntryPos == 1)
3581  {
3582  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3583  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3584  }
3585  else // == 3
3586  {
3587  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3588  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3589  }
3590  }
3591  else
3592  {
3593  if(EntryPos > 1)
3594  {
3595  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3596  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3597  }
3598  else
3599  {
3600  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3601  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3602  }
3603  }
3604  EntryHalfLength = CurrentElementHalfLength;
3605  FrontElementLength = 2 * CurrentElementHalfLength;
3606  }
3607  else
3608  {
3609  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3610  }
3611  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3612  {
3613  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3614  }
3615  // check if zero entry speed with another train directly in front & if so remain stopped
3616  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3617  {
3618  EntrySpeed = 0;
3619  ExitSpeedHalf = 0;
3620  ExitSpeedFull = 0;
3621  MaxExitSpeed = 0;
3622  BrakeRate = 0;
3623  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3624  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3625  StoppedForTrainInFront = true;
3626  Utilities->CallLogPop(705);
3627  return;
3628  }
3629  // new at v2.4.0 - check for stopped and zero power
3630  if((EntrySpeed < 1) && PowerAtRail < 1)
3631  {
3632  EntrySpeed = 0;
3633  ExitSpeedHalf = 0;
3634  ExitSpeedFull = 0;
3635  MaxExitSpeed = 0;
3636  BrakeRate = 0;
3637  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3638  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3639  StoppedWithoutPower = true;
3640  Utilities->CallLogPop(2125);
3641  return;
3642  }
3643 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3644  if(BeingCalledOn)
3645  {
3646  LimitingSpeed = CallOnMaxSpeed;
3647  }
3648  else
3649  {
3650  LimitingSpeed = MaximumSpeedLimit;
3651  }
3652  if(LimitingSpeed > FrontElementSpeedLimit)
3653  {
3654  LimitingSpeed = FrontElementSpeedLimit;
3655  }
3656  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3657  {
3658  LimitingSpeed = MaxRunningSpeed;
3659  }
3660  FrontElementMaxSpeed = LimitingSpeed;
3661 
3662 /*
3663  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3664  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3665  (2) V/3.6 = U/3.6 - FT;
3666  (3) S = UT/3.6 - 0.5FT^2
3667  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3668 
3669  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3670  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3671  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3672  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3673  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3674 
3675  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3676  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3677  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3678 */
3679 
3680 // check if running past a red signal without permission
3681  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3682  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3683  {
3684  SPADFlag = true; // user has to intervene to reset & restart after spad
3685  }
3686  if(!SPADFlag)
3687  {
3688  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3689  // to begin with calc the maximum exit speed (assumes accelerating) and then reduce it if necessary
3690  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3691  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
3692  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
3693  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3694 
3695  double ExitSpeedAtMaxBraking;
3696  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3697  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3699  {
3700  ExitSpeedAtMaxBraking = 0;
3701  }
3702  else
3703  {
3704  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3705  }
3706  double SpeedToUse;
3707  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added at v2.4.2 because trains entering at a continuation with zero (or very low) speed
3708  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3709  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3710  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3711  {
3712  SpeedToUse = ExitSpeedAtMaxBraking;
3713  }
3714  else
3715  {
3716  SpeedToUse = LimitingSpeed;
3717  }
3718  if(ExitSpeedFull > SpeedToUse)
3719  {
3720  ExitSpeedFull = SpeedToUse;
3721  }
3722  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3723  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3724 
3725  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3726  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3727  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3728 
3729  do
3730  {
3731  RedSignalFlag = false;
3732  BuffersFlag = false;
3733  StationFlag = false;
3734  BuffersOrContinuationNowFlag = false;
3735  ContinuationNextFlag = false;
3736  // have to reset this after the above test
3737  // add current element length to CumulativeLength
3738  CumulativeLength += (2 * CurrentElementHalfLength);
3739  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3740  {
3741  SignallerStopRequired = true;
3742  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3743  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3744  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3745  if(SignallerStopBrakeRate < TempBR)
3746  {
3747  SignallerStopBrakeRate = TempBR;
3748  }
3749  }
3750  // first check for stops within the length of the current element, where don't want any more checks & don't want
3751  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3752  // during the last loop when the NextTrackVectorPosition was the signal.
3753 
3754  // check if current element is a buffer
3755  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3756  {
3757  // no need to add in the length of this element to CumulativeLength as already included
3758  BuffersFlag = true;
3759  }
3760  // check if current element is a station stop
3761  if(TrainMode == Timetable)
3762  {
3763  bool StopRequired = false;
3764  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3765  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3766  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3767  {
3768  // no need to add in the length of element to CumulativeLength
3769  if(StopRequired)
3770  {
3771  StationFlag = true;
3772  }
3773  }
3774  }
3775  else
3776  {
3777  StationFlag = false;
3778  }
3779  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3780  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3781  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3782  {
3783  BuffersOrContinuationNowFlag = true;
3784  }
3785  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3786  {
3787  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3788  {
3789  if((EntryPos == 0) || (EntryPos == 2))
3790  {
3791  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3792  {
3793  ExitPos = 1;
3794  }
3795  else
3796  {
3797  ExitPos = 3;
3798  }
3799  }
3800  else
3801  {
3802  ExitPos = 0;
3803  }
3804  }
3805  else
3806  {
3807  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
3808  }
3809  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
3810  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
3811  if(NextTrackVectorPosition > -1)
3812  {
3813  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
3814  // this test & section added at v0.6
3815  {
3816  if((NextEntryPos == 0) || (NextEntryPos == 2))
3817  {
3818  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
3819  {
3820  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
3821  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
3822  }
3823  else
3824  {
3825  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
3826  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
3827  }
3828  }
3829  else if(NextEntryPos == 1)
3830  {
3831  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
3832  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
3833  }
3834  else // == 3
3835  {
3836  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
3837  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
3838  }
3839  }
3840  else
3841  {
3842  if(NextEntryPos > 1)
3843  {
3844  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
3845  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
3846  }
3847  else
3848  {
3849  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
3850  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
3851  }
3852  }
3853  }
3854  else
3855  {
3856  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
3857  }
3858  // now check for stops, first cover those where don't want to add in length of next element
3859  // check if next element is a red signal - Attr 0,
3860  // note that this doesn't apply to trains stopped at a red signal since the signal position is
3861  // CurrentTrackVectorPosition not NextTrackVectorPosition
3862  bool StopRequired;
3863  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
3864  {
3865  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
3866  {
3867  // no need to add in the length of element to CumulativeLength
3868  RedSignalFlag = true;
3869  }
3870  // next element is a red signal
3871  }
3872  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
3873  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
3874  // at least one platform element free
3876  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
3877  NextEntryPos, TrainID))
3878  {
3879  // no need to add in the length of element to CumulativeLength
3880  if(StopRequired)
3881  {
3882  StationFlag = true;
3883  }
3884  }
3885  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
3886  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
3887  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
3888  {
3889  // no need to add in the length of element to CumulativeLength
3890  TrainInFrontInSignallerModeFlag = true;
3891  }
3892  // check if next element is a buffer
3893  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
3894  {
3895  // need to add in the length of that element to CumulativeLength
3896  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
3897  BuffersFlag = true;
3898  }
3899  // check if next element is a station stop
3901  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
3902  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
3903  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3904  {
3905  // need to add in the length of that element to CumulativeLength if a stop required
3906  if(StopRequired)
3907  {
3908  StationFlag = true;
3909  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
3910  }
3911  }
3912  }
3913  //now can decide whether need to stop over CumulativeLength
3914  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
3915  {
3916  // have to come to a stop over CumulativeLength
3917  if(CumulativeLength == FrontElementLength)
3918  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
3919  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
3920  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
3921  // and if less than EntrySpeed then skip this section (don't need any acceleration)
3922  // if not calc speed at halfway point & if less than above set half speed to this value;
3923  // use constant acceleration in calculating half time point
3924  {
3925  MaxExitSpeed = 0;
3926  double MaxHalfSpeed;
3927  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
3928  // have to halve the element length, & can't be zero or negative so no need to test
3929  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3930  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
3931  {
3932  MaxHalfSpeed = FrontElementMaxSpeed;
3933  }
3934  else
3935  {
3936  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3937  }
3938  if(MaxHalfSpeed > (2 * EntrySpeed))
3939  // use 2x to prevent kangarooing at last element when had
3940  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
3941  {
3942  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3943  0.333334);
3944  bool HalfSpeedLimited = false;
3945  if(MaxHalfSpeed < ExitSpeedHalf)
3946  {
3947  ExitSpeedHalf = MaxHalfSpeed;
3948  HalfSpeedLimited = true;
3949  }
3950  if(PowerAtRail > 1)
3951  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
3952  {
3953  // [km/h/3.6 = m/s]
3954  ExitTimeHalf =
3955  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3956  }
3957  else
3958  {
3959  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
3960  }
3961  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
3962  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
3963  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
3964  // by a long braking period
3965  ExitSpeedFull = 0;
3966  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
3967  if(TempBrakeRate > MaxBrakeRate)
3968  {
3969  TempBrakeRate = MaxBrakeRate;
3970  }
3971  // shouldn't be but leave in anyway
3972  if(TempBrakeRate > BrakeRate)
3973  {
3974  BrakeRate = TempBrakeRate;
3975  }
3976  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3977  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
3978  if(HalfSpeedLimited)
3979  // this is the change referred to above
3980  {
3981  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
3982  ExitTimeHalf = EntryTime + BrakingTime;
3983  ExitTimeFull = ExitTimeHalf + BrakingTime;
3984  }
3985  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
3986  Utilities->CallLogPop(1095);
3987  return;
3988  }
3989  }
3990  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
3991  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
3992  // calc th, tf, sh, & sf
3993  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3994  if(TempBrakeRate > MaxBrakeRate)
3995  {
3996  TempBrakeRate = MaxBrakeRate;
3997  }
3998  if(TempBrakeRate > BrakeRate)
3999  {
4000  BrakeRate = TempBrakeRate;
4001  }
4002  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4003  if(SignallerStopRequired)
4004  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
4005  {
4007  {
4009  // this prevents the brakerate from reducing for a signaller stop
4010  // regardless of other conditions that may change as progress round the loop
4011  }
4012  }
4014  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
4015  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
4016  {
4018  }
4019  int TempMaxExitSpeed;
4020  // calc current value & if less than MaxExitSpeed set that to this
4021  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
4022  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4023  {
4024  MaxExitSpeedAtHalfBraking = 0;
4025  }
4026  else
4027  {
4028  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4029  }
4030  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
4031  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
4032  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4033  {
4034  TempMaxExitSpeed = FrontElementMaxSpeed;
4035  }
4036  else
4037  {
4038  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4039  }
4040  if(TempMaxExitSpeed < MaxExitSpeed)
4041  {
4042  MaxExitSpeed = TempMaxExitSpeed;
4043  }
4044  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
4045  // Cumulativelength, and Cumulativelength
4046 
4047  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
4048  {
4049  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4050  if(ExitSpeedHalfSquared < 10)
4051  {
4052  ExitSpeedHalf = 0;
4053  }
4054  else
4055  {
4056  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4057  }
4058  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4059  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4060  if(ExitSpeedFullSquared < 10)
4061  {
4062  ExitSpeedFull = 0;
4063  }
4064  else
4065  {
4066  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4067  }
4068  if((StationFlag) && (CumulativeLength == FrontElementLength))
4069  {
4070  ExitSpeedFull = 0;
4071  // force a stop for station (not for buffers or red signal)
4072  }
4073  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4074  }
4075  // new condition at v2.4.0
4076  else if(PowerAtRail <= 1)
4077  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4078  // avoid using AValue in denominator or have excessively long times
4079  {
4080  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4081  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4082  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4083 
4084  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4085  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4086  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4087  }
4088  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4089  // without the power need above condition or have hours of delay times, above added at v2.4.0
4090  {
4091  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4092  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4093  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4094  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4095  BrakeRate = 0;
4096  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4097  0.333334);
4098  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4099  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4101  // can accelerate continually over the half length
4102  {
4103  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4104  / 86400.0);
4106  // can accelerate continually over the full length
4107  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4108  {
4109  ExitTimeFull =
4110  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4111  }
4112  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4113  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4114  {
4115  // added at v0.6 as a safeguard
4116  if(MaxExitSpeed < EntrySpeed)
4117  {
4119  }
4120  // to prevent DeltaExitTimeToMaxInSecs being negative
4121  if(MaxExitSpeed < 1)
4122  {
4123  MaxExitSpeed = 1;
4124  }
4125  // to prevent divide by zero error
4126  // below as was
4128  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4129  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4130  (1.5 * AValue * AValue);
4131  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4132  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4133  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4134  }
4135  }
4136  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4137  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4138  // second halves of the element
4139  {
4140  // added at v0.6 as a safeguard
4141  if(MaxExitSpeed < EntrySpeed)
4142  {
4144  }
4145  // to prevent DeltaExitTimeToMaxInSecs being negative
4146  if(MaxExitSpeed < 1)
4147  {
4148  MaxExitSpeed = 1; // to prevent divide by zero error
4149  }
4150  // below as was
4152  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4153  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4154  (1.5 * AValue * AValue);
4155  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4156  // remaining distance to half length
4157  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4158  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4160  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4161  }
4162  }
4163  Utilities->CallLogPop(706);
4164  return;
4165  }
4166  else
4167  {
4168  if(!BuffersOrContinuationNowFlag)
4169  {
4170  if(NextSpeedLimit < LimitingSpeed)
4171  {
4172  LimitingSpeed = NextSpeedLimit;
4173  }
4174  }
4175  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4176  int TempMaxExitSpeed;
4177  // calc current value & if less than MaxExitSpeed set that to this
4178  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4179  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4180  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4181  {
4182  MaxExitSpeedAtHalfBraking = 0;
4183  }
4184  else
4185  {
4186  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4187  }
4188  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4189  {
4190  TempMaxExitSpeed = FrontElementMaxSpeed;
4191  }
4192  else
4193  {
4194  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4195  }
4196  if(TempMaxExitSpeed < MaxExitSpeed)
4197  {
4198  MaxExitSpeed = TempMaxExitSpeed;
4199  }
4200  // MaxExitSpeed is an external variable & this can reduce its value
4201  if(EntrySpeed > LimitingSpeed)
4202  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4203  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4204  {
4205  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4206  if(TempBrakeRate > MaxBrakeRate)
4207  {
4208  TempBrakeRate = MaxBrakeRate;
4209  }
4210  // shouldn't be for speedlimits since all known in advance, but include anyway
4211  if(TempBrakeRate > BrakeRate)
4212  {
4213  BrakeRate = TempBrakeRate;
4214  }
4215  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4216  }
4217  }
4218  if(!BuffersOrContinuationNowFlag)
4219  {
4220  CurrentTrackVectorPosition = NextTrackVectorPosition;
4221  EntryPos = NextEntryPos;
4222  CurrentElementHalfLength = NextElementHalfLength;
4223  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4224  {
4225  ContinuationNextFlag = true;
4226  }
4227  }
4228  }
4229  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4231  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4232  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4233  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4234  // stopping distance after it.
4235 
4236  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4237  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4238 
4239  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4240  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4241  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4242  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4243  // too late
4244 
4245  // set final braking or acc'n speed & time values
4246  if(BrakeRate > 0.01)
4247  {
4248  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4249  if(ExitSpeedHalfSquared < 10)
4250  {
4251  ExitSpeedHalf = 0;
4252  }
4253  else
4254  {
4255  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4256  }
4257  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4258  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4259  if(ExitSpeedFullSquared < 10)
4260  {
4261  ExitSpeedFull = 0;
4262  }
4263  else
4264  {
4265  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4266  }
4267  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4268  }
4269  else
4270  {
4271  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4272  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4273  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4274  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4275 
4276  BrakeRate = 0;
4277  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4278  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4279  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4281  {
4282  if(PowerAtRail > 1)
4283  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4284  {
4285  // [km/h/3.6 = m/s]
4286  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4287  / 86400.0);
4288  }
4289  else
4290  {
4291  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4292  }
4294  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4295  {
4296  if(PowerAtRail > 1)
4297  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4298  {
4299  // [km/h/3.6 = m/s]
4300  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4301  / 86400.0);
4302  }
4303  else
4304  {
4305  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4306  }
4307  }
4308  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4309  {
4310  // added at v0.6 as a safeguard
4311  if(MaxExitSpeed < EntrySpeed)
4312  {
4314  }
4315  // to prevent DeltaExitTimeToMaxInSecs being negative
4316  if(MaxExitSpeed < 1)
4317  {
4318  MaxExitSpeed = 1; // to prevent divide by zero error
4319  }
4320  // below as was
4322  double DeltaExitTimeToMaxInSecs;
4323  double DistanceToMax;
4324  if(PowerAtRail > 1) // added at v2.4.0
4325  {
4326  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4327  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4328  (1.5 * AValue * AValue);
4329  }
4330  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4331  {
4332  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4333  // these not really accurate but will be good enough
4334  DistanceToMax = EntryHalfLength;
4335  }
4336  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4337  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4338  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4339  }
4340  }
4341  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4342  {
4343  // added at v0.6 as a safeguard
4344  if(MaxExitSpeed < EntrySpeed)
4345  {
4347  }
4348  // to prevent DeltaExitTimeToMaxInSecs being negative
4349  if(MaxExitSpeed < 1)
4350  {
4351  MaxExitSpeed = 1; // to prevent divide by zero error
4352  }
4353  // below as was
4355  double DeltaExitTimeToMaxInSecs;
4356  double DistanceToMax;
4357  if(PowerAtRail > 1) // added at v2.4.0
4358  {
4359  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4360  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4361  (1.5 * AValue * AValue);
4362  }
4363  else
4364  {
4365  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4366  // these not really accurate but will be good enough
4367  DistanceToMax = EntryHalfLength / 2;
4368  }
4369  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4370  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4371  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4373  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4374  }
4375  }
4376  }
4377 
4378  else // SPADFlag set
4379  {
4381  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4382  if(ExitSpeedHalfSquared < 10)
4383  {
4384  ExitSpeedHalf = 0;
4385  }
4386  else
4387  {
4388  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4389  }
4390  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4391  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4392  if(ExitSpeedFullSquared < 10)
4393  {
4394  ExitSpeedFull = 0;
4395  }
4396  else
4397  {
4398  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4399  }
4400  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4401 
4402  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4403  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4404  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4405  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4406  // will be the stopping speed.
4407  if(ExitSpeedFull > 0)
4408  {
4409  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4410  {
4411  if((EntryPos == 0) || (EntryPos == 2))
4412  {
4413  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4414  {
4415  ExitPos = 1;
4416  }
4417  else
4418  {
4419  ExitPos = 3;
4420  }
4421  }
4422  else
4423  {
4424  ExitPos = 0;
4425  }
4426  }
4427  else
4428  {
4429  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4430  }
4431  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4432  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4433  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4434  {
4435  int NextElementLength;
4436  if(NextEntryPos > 1)
4437  {
4438  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4439  }
4440  else
4441  {
4442  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4443  }
4444  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4445  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4446  {
4447  ExitSpeedFull = 0;
4448  }
4449  }
4450  }
4451  }
4452  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4453  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4454  {
4455  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4456  if(LeadElement > -1)
4457  {
4459  // don't stop on a continuation either entering or leaving
4460  {
4463  MaxExitSpeed = LimitingSpeed;
4464  BrakeRate = 0;
4465  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4466  // assume length is 50m for ease of calc
4467  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4468  Utilities->CallLogPop(2126);
4469  return;
4470  }
4471  }
4472  else if(MidElement > -1)
4473  {
4475  {
4478  MaxExitSpeed = LimitingSpeed;
4479  BrakeRate = 0;
4480  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4481  // assume length is 50m for ease of calc
4482  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4483  Utilities->CallLogPop(2127);
4484  return;
4485  }
4486  }
4487  else if(LagElement > -1)
4488  {
4490  {
4493  MaxExitSpeed = LimitingSpeed;
4494  BrakeRate = 0;
4495  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4496  // assume length is 50m for ease of calc
4497  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4498  Utilities->CallLogPop(2128);
4499  return;
4500  }
4501  }
4502  if(EntrySpeed > 7.5) // keep going for at least another element
4503  {
4504  ExitSpeedHalf = EntrySpeed - 2.5;
4505  ExitSpeedFull = EntrySpeed - 5;
4506  MaxExitSpeed = LimitingSpeed;
4507  BrakeRate = 0;
4508  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4509  // assume length is 50m for ease of calc
4510  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4511  Utilities->CallLogPop(2129);
4512  return;
4513  }
4514  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4515  {
4516  // will appear to have slowed at steady rate
4517  ExitSpeedHalf = 0;
4518  ExitSpeedFull = 0;
4519  MaxExitSpeed = LimitingSpeed;
4520  BrakeRate = 0;
4521  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4522  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4523  StoppedWithoutPower = true;
4524  Utilities->CallLogPop(2130);
4525  return;
4526  }
4527  }
4528  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4529  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4530  Utilities->CallLogPop(707);
4531 }
4532 // ---------------------------------------------------------------------------
4533 /*
4534  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4535  {
4536  int NextExitPos;
4537  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4538  if(TimetableVector.empty()) return false;
4539  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4540  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4541  {
4542  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4543  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4544  {
4545  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4546  }
4547  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4548  {
4549  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4550  }
4551  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4552  NextElement = TempElement;
4553  }
4554  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4555  if(NextElement.TrackType == Buffers) return true;
4556  return false;//shouldn't reach here but include to prevent compiler return warning
4557  }
4558 */
4559 // ---------------------------------------------------------------------------
4560 
4561 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4562 /*
4563  returns the number by which the train ActionVectorEntryPtr needs
4564  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4565  actions when a stop or pass location has been reached before other timetabled events have been carried out. If can't find it, or Name
4566  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4567  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4568  or pass (false) the location.
4569 */{
4570  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4571  Stop = false;
4572  if(TimetableFinished || (Name == ""))
4573  {
4574  Utilities->CallLogPop(957);
4575  return(-1);
4576  }
4578 /*added the following check at v2.11.1 because of a fault found in Kevin Smith's railway (notified 02/01/22). CH01 started at
4579 Chester behind the stop position, but when it departed and this function was called it found Chester again with no cdt
4580 before it (it went round the Liverpool Loop), so it stopped again when it reached the stop position and reported all intermediate stops as having been
4581 missed. This check is for the train just having departed from the station in question, and if it has then any further stations
4582 with the same name are ignored - i.e. it stops a train from stopping at the same station twice in succession.
4583 */
4584  if(Ptr > &TrainDataEntryPtr->ActionVector.at(0))
4585  {
4586  Ptr--;
4587  if((Ptr->DepartureTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4588  {
4589  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4590  {
4591  Utilities->CallLogPop(2444);
4592  return(-1);
4593  }
4594  }
4595  }
4596  // start looking from current pointer position
4597  for(TActionVectorEntry *Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4598  {
4599  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4600  {
4601  break;
4602  }
4603  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4604  {
4605  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4606  {
4607  Stop = true;
4608  Utilities->CallLogPop(960);
4609  return (Ptr - ActionVectorEntryPtr);
4610  }
4611  }
4612  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4613  {
4614  Utilities->CallLogPop(1517);
4615  return (Ptr - ActionVectorEntryPtr);
4616  }
4617  }
4618  Utilities->CallLogPop(959);
4619  return(-1); // not found a valid entry
4620 }
4621 
4622 // ---------------------------------------------------------------------------
4623 
4625 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4626  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4627  Ignores the call-on signal.
4628 */{
4629  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4630  int ReturnVal = 0;
4631 
4632  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4633  {
4634  Track->TrackElementAt(1031, x).TempTrackMarker01 = false;
4635  Track->TrackElementAt(1032, x).TempTrackMarker23 = false;
4636  }
4637  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4638  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4639 
4640  while(true)
4641  {
4642  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4643  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4644  {
4645  ReturnVal = 1;
4646  break;
4647  }
4648  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4649  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4650  {
4651  ReturnVal = 2;
4652  break;
4653  }
4654  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4655  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4656  {
4657  ReturnVal = 3;
4658  break;
4659  }
4660  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388,
4661  CurrentTrackVectorPosition).TrackType == Crossover))
4662  {
4663  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4664  // must be a loop - reached same point as examined earlier
4665  {
4666  ReturnVal = 4;
4667  break;
4668  }
4669  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4670  {
4671  ReturnVal = 4;
4672  break;
4673  }
4674  }
4675  else
4676  {
4677  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526,
4678  CurrentTrackVectorPosition).TempTrackMarker23))
4679  {
4680  ReturnVal = 4;
4681  break;
4682  }
4683  }
4684  if(EntryPos < 2)
4685  {
4686  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4687  }
4688  else
4689  {
4690  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4691  }
4692  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4693  {
4694  if((EntryPos == 0) || (EntryPos == 2))
4695  {
4696  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4697  {
4698  ExitPos = 1;
4699  }
4700  else
4701  {
4702  ExitPos = 3;
4703  }
4704  }
4705  else
4706  {
4707  ExitPos = 0;
4708  }
4709  }
4710  else
4711  {
4712  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4713  }
4714  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4715  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4716  CurrentTrackVectorPosition = NextTrackVectorPosition;
4717  EntryPos = NextEntryPos;
4718  }
4719  if(ReturnVal == 1)
4720  {
4721  Utilities->CallLogPop(708);
4722  return(false);
4723  }
4724  if(ReturnVal == 2)
4725  {
4726  Utilities->CallLogPop(709);
4727  return(true);
4728  }
4729  if(ReturnVal == 3)
4730  {
4731  Utilities->CallLogPop(946);
4732  return(true);
4733  }
4734  if(ReturnVal == 4)
4735  {
4736  Utilities->CallLogPop(947);
4737  return(true);
4738  }
4739  else
4740  {
4741  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4742  }
4743 }
4744 
4745 // ---------------------------------------------------------------------------
4746 
4748 /*
4749  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4750  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4751  change of direction (cdt), remaining here (Frh), or under signaller control);
4752  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4753  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4754  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4755  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] l) no existing route conflicts with the route into the platform,
4756  m) not failed or stopped without power
4757  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4758  change points outside the route or have a route conflict if another route is set.
4759 */{
4760  if(Track->RouteFlashFlag || TrainFailed || StoppedWithoutPower) //failed & no power conditions added at v2.10.0
4761  {
4762  return(false); // don't want to create a new route from the stop signal if one is already in construction & can't call on if failed or no power
4763  }
4764  // some of the callingon route elements may be involved
4765  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4766  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4767  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4768  int RouteStartPosition;
4769  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4770  int PlatformPosition;
4771  // the track vector position of the first stop platfrom
4772  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4773  // not used here
4774  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4775  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
4776  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
4777  // must be stopped at a signal but not at a location & still in timetable (a)
4779  // no need to check for SignallerStopped as this function only called in Timetable mode
4780  {
4781  Utilities->CallLogPop(711);
4782  return(false);
4783  }
4784  while(true)
4785  {
4786  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4787  // don't look further than 4km ahead (j)
4788  if(Distance > (4000 + LeadElementDistance))
4789  {
4790  Utilities->CallLogPop(967);
4791  return(false);
4792  }
4793  // if find another train on an element in front, before find a valid platform, return false (c)
4794  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
4795  {
4796  Utilities->CallLogPop(713);
4797  return(false);
4798  }
4799  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
4800  // be facing later on)
4801  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
4802  {
4803  // get LeadElement, if -1 return (could be exiting at continuation) (i)
4804  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
4805  if(OtherTrain.LeadElement == -1)
4806  {
4807  Utilities->CallLogPop(714);
4808  return(false);
4809  }
4810  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
4811  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
4812  {
4813  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
4814  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
4815  (OtherTrain.TrainMode == Signaller))
4816  {
4817  break;
4818  }
4819  else
4820  {
4821  Utilities->CallLogPop(955);
4822  return(false);
4823  }
4824  }
4825  else // (h)
4826  {
4827  break;
4828  }
4829  }
4830  // if reach buffers or exit continuation return false (can set route)
4831  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
4832  {
4833  Utilities->CallLogPop(716);
4834  return(false);
4835  }
4836  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
4837  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
4839  {
4840  Utilities->CallLogPop(717);
4841  return(false);
4842  }
4843  // if reach a location that isn't in timetable return false - drop this as still can't set a route
4844 /*
4845  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
4846  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
4847  {
4848  Utilities->CallLogPop(718);
4849  return false;
4850  }
4851 */
4852  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
4853  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
4854  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
4855  {
4856  if(StopRequired)
4857  {
4858  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
4859  {
4860  if(!PlatformFoundFlag)
4861  {
4862  PlatformPosition = CurrentTrackVectorPosition;
4863  }
4864  // ensure this only set once at first valid platform position - the unrestricted route will end here
4865  PlatformFoundFlag = true;
4866  }
4867  }
4868  }
4869  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
4870  // train has to be at station but that has to be before the next forward signal
4871 /*
4872  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
4873  {
4874  Utilities->CallLogPop(719);
4875  return false;
4876  }
4877 */
4878  // make sure points are followed correctly (d) & set ExitPos
4879  if(CurrentTrackElement.TrackType == Points)
4880  {
4881  if((EntryPos == 0) || (EntryPos == 2))
4882  {
4883  if(CurrentTrackElement.Attribute == 0)
4884  {
4885  ExitPos = 1;
4886  }
4887  else
4888  {
4889  ExitPos = 3;
4890  }
4891  }
4892  if(EntryPos == 1)
4893  {
4894  if(CurrentTrackElement.Attribute == 0)
4895  {
4896  ExitPos = 0;
4897  }
4898  else
4899  {
4900  Utilities->CallLogPop(720);
4901  return(false);
4902  }
4903  }
4904  if(EntryPos == 3)
4905  {
4906  if(CurrentTrackElement.Attribute == 1)
4907  {
4908  ExitPos = 0;
4909  }
4910  else
4911  {
4912  Utilities->CallLogPop(721);
4913  return(false);
4914  }
4915  }
4916  }
4917  else
4918  {
4919  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4920  }
4921  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
4922  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
4923  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
4924  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
4925  if(ElementNumber < 2)
4926  {
4927  SkipRouteCheck = true;
4928  }
4929  else
4930  {
4931  SkipRouteCheck = false;
4932  }
4933  if(ElementNumber == 1) // the stop signal
4934  {
4935  RouteStartPosition = CurrentTrackVectorPosition;
4936  }
4937 /*
4938  if(ElementNumber == 2)
4939  {
4940  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
4941  else AutoSigs = false;
4942  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
4943  }
4944 */
4945  if(ElementNumber > 1)
4946  {
4947  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
4948  {
4949  RouteOrPartRouteSet = true;
4950  }
4951  else
4952  {
4953  RouteOrPartRouteSet = false;
4954  }
4955  }
4956  if(!SkipRouteCheck && !RouteOrPartRouteSet)
4957  {
4958  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
4959  {
4960  Utilities->CallLogPop(1859);
4961  return(false);
4962  }
4963  int ExitLink = CurrentTrackElement.Link[ExitPos];
4964  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
4965  {
4966  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
4967  {
4968  Utilities->CallLogPop(1850);
4969  return(false);
4970  }
4971  }
4972  }
4973  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
4974  if(EntryPos < 2)
4975  {
4976  Distance += CurrentTrackElement.Length01;
4977  }
4978  else
4979  {
4980  Distance += CurrentTrackElement.Length23;
4981  }
4982  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
4983  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
4984  CurrentTrackVectorPosition = NextTrackVectorPosition;
4985  EntryPos = NextEntryPos;
4986  ElementNumber++;
4987  } // while(true)
4988 
4989  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
4990  // from the stop signal (note that it may be last in an autosigs route)
4991  // a single element route at the stop signal should have been removed prior to this function being called (that called before
4992  // this in ClockTimer2)
4993 
4994  // now add elements to the CallonVector
4995  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
4996 
4997  AllRoutes->CallonVector.push_back(CallonEntry);
4998  Utilities->CallLogPop(1860);
4999  return(true); // return false if fail to set route for any reason
5000 }
5001 
5002 // ---------------------------------------------------------------------------
5003 /*
5004  bool TTrain::TimetableFinished()
5005  {
5006  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
5007  {
5008  return true;
5009  }
5010  return false;
5011  }
5012 */
5013 // ---------------------------------------------------------------------------
5014 
5015 AnsiString TTrain::GetTrainHeadCode(int Caller)
5016 
5017 {
5018  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
5019  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
5020 
5021  Utilities->CallLogPop(1452);
5022  return(RepeatHeadCode);
5023 }
5024 
5025 // ---------------------------------------------------------------------------
5026 
5027 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
5028 {
5029  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
5030  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
5031 
5032  Utilities->CallLogPop(1453);
5033  return(RepeatTime);
5034 }
5035 
5036 // ---------------------------------------------------------------------------
5037 
5038 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
5039 {
5040  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
5041  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
5042  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
5043  // first check that train is fully on the railway
5044  bool FrontValid = false, RearValid = false;
5045  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
5046 
5047  if((LeadElement == -1) || (MidElement == -1))
5048  {
5049  TrainToBeJoinedBy = NULL;
5050  Utilities->CallLogPop(2131);
5051  return(false);
5052  }
5054  {
5055  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
5056  FrontValid = true;
5057  }
5059  {
5060  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
5061  RearValid = true;
5062  }
5063  int TrainToBeJoinedByID = -1;
5064 
5065  // first check if on a 2-track element & select correct ID number if so
5066  if(FrontValid)
5067  {
5068  if(FrontAdjacentTrackElement.TrackType == Bridge)
5069  {
5071  {
5072  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5073  }
5074  else
5075  {
5076  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5077  }
5078  }
5079  else
5080  {
5081  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
5082  }
5083  }
5084  if((TrainToBeJoinedByID < 0) && RearValid)
5085  {
5086  // first check if on a 2-track element & select correct ID number if so
5087  if(RearAdjacentTrackElement.TrackType == Bridge)
5088  {
5090  {
5091  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5092  }
5093  else
5094  {
5095  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5096  }
5097  }
5098  else
5099  {
5100  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5101  }
5102  }
5103  if(TrainToBeJoinedByID < 0) // no adjacent train
5104  {
5105  TrainToBeJoinedBy = NULL;
5106  Utilities->CallLogPop(2132);
5107  return(false);
5108  }
5109  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5110  if(!TrainToBeJoinedBy->Stopped())
5111  {
5112  TrainToBeJoinedBy = NULL;
5113  Utilities->CallLogPop(2133);
5114  return(false);
5115  }
5116  Utilities->CallLogPop(2134);
5117  return(true);
5118 }
5119 
5120 // ---------------------------------------------------------------------------
5121 
5122 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
5123  TDateTime TimetableNonRepeatTime, bool Warning)
5124 /*
5125  Time = timetable time, the time adjustments for repeat trains is carried out internally
5126  Not all messages need this, if not needed a dummy value is required but not used
5127 
5128  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5129  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5130  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5131  //NB for Frh just give terminated message but without event time - don't use this function
5132  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5133  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5134  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5135  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5136  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
5137  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
5138  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5139  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5140  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5141  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5142  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5143  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5144  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5145  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5146  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5147  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5148  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass red signal
5149  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5150  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5151  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5152  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5153  SignallerStop 06:05:40: 2F46 stopped on signaller command
5154  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5155  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5156 */{
5157  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5158  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5159  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5160  int IntMinsLate = 0;
5161 
5162  // need to set it in case MinsLate == 0, since it isn't tested for that
5163  if(ActionType == Arrive)
5164  {
5165  ActionLog = " arrived at ";
5166  }
5167  if(ActionType == Terminate)
5168  {
5169  if(TerminatedMessageSent) // to avoid it being sent twice
5170  {
5171  Utilities->CallLogPop(1104);
5172  return;
5173  }
5174  ActionLog = " terminated at ";
5175  TerminatedMessageSent = true;
5176  }
5177  if(ActionType == Depart)
5178  {
5179  ActionLog = " departed from ";
5180  }
5181  if(ActionType == Pass)
5182  {
5183  ActionLog = " passed ";
5184  }
5185  if(ActionType == Create)
5186  {
5187  ActionLog = " created at ";
5188  }
5189  if(ActionType == Enter)
5190  {
5191  ActionLog = " entered railway at ";
5192  }
5193  if(ActionType == Leave)
5194  {
5195  ActionLog = " left railway at ";
5196  }
5197  if(ActionType == FrontSplit)
5198  {
5199  ActionLog = " split from front to ";
5200  }
5201  if(ActionType == RearSplit)
5202  {
5203  ActionLog = " split from rear to ";
5204  }
5205  if(ActionType == JoinedByOther)
5206  {
5207  ActionLog = " joined by ";
5208  }
5209  if(ActionType == ChangeDirection)
5210  {
5211  ActionLog = " changed direction at ";
5212  }
5213  if(ActionType == NewService)
5214  {
5215  ActionLog = " became new service ";
5216  }
5217  if(ActionType == TakeSignallerControl)
5218  {
5219  ActionLog = " taken under signaller control at ";
5220  }
5221  if(ActionType == RestoreTimetableControl)
5222  {
5223  ActionLog = " restored to timetable control at ";
5224  }
5225  if(ActionType == RemoveTrain)
5226  {
5227  if(Crashed)
5228  {
5229  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5230  }
5231  else if(Derailed)
5232  {
5233  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5234  }
5235  else
5236  {
5237  ActionLog = " REMOVED FROM RAILWAY at ";
5238  }
5239  }
5240  if(ActionType == SignallerMoveForwards)
5241  {
5242  ActionLog = " received signaller authority to proceed";
5243  }
5244  if(ActionType == SignallerStepForward)
5245  {
5246  ActionLog = " received signaller authority to step forward";
5247  }
5248  if(ActionType == SignallerChangeDirection)
5249  {
5250  ActionLog = " changed direction under signaller control at ";
5251  }
5252  if(ActionType == SignallerPassRedSignal)
5253  {
5254  ActionLog = " received signaller authority to pass red signal";
5255  }
5256  if(ActionType == SignallerControlStop)
5257  {
5258  ActionLog = " received signaller instruction to stop";
5259  }
5260  if(ActionType == SignallerStop)
5261  {
5262  ActionLog = " stopped on signaller instruction ";
5263  }
5264  if(ActionType == SignallerJoin)
5265  {
5266  ActionLog = " joined under signaller control by ";
5267  }
5268  if(ActionType == TrainFailure)
5269  {
5270  ActionLog = " suffered an onboard power failure at ";
5271  }
5272  if(ActionType == RepairFailedTrain)
5273  {
5274  ActionLog = " failure repaired at ";
5275  }
5276  if(ActionType == SignallerLeave)
5277  {
5278  ActionLog = " left railway under signaller control at ";
5279  }
5280  if(OtherHeadCode != "")
5281  {
5282  OtherHeadCode += " at ";
5283  }
5284  TDateTime ActualTime = TrainController->TTClockTime;
5285 
5286  if(Warning)
5287  {
5288  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5289  WarningBaseLog = HeadCode + ActionLog + OtherHeadCode + LocationName;
5290  }
5291  else
5292  {
5293  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5294  }
5295  bool TimePerformance = true;
5296 
5297  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5298  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5299  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5300  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5301  // SignallerJoin & RepairFailedTrain new at v2.4.0
5302  {
5303  TimePerformance = false;
5304  }
5305  if(TimePerformance)
5306  {
5307  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5308  MinsDelayed = float(MinsLate);
5309  if(ActionType == Pass) //added at v2.9.2 to prevent time to act increasing suddenly for early pass times then becoming 'NOW' when stops at signal
5310  {
5311  MinsDelayed = 0;
5312  }
5313  // new v2.2.0 for OpActionPanel, can be positive or negative
5314  if(ActionType == Arrive)
5315  {
5317  }
5318  // since train has just arrived this value is the
5319  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5320  // subtracted from later stop recoverable times.
5321  if(MinsLate < 0)
5322  {
5323  IntMinsLate = int(ceil(MinsLate));
5324  }
5325  if(MinsLate > 0)
5326  {
5327  IntMinsLate = int(floor(MinsLate));
5328  }
5329  if(IntMinsLate == 0)
5330  {
5331  PerfLog = " on time";
5332  }
5333  else if(IntMinsLate == 1)
5334  {
5335  PerfLog = " 1 minute late";
5336  }
5337  else if(IntMinsLate == -1)
5338  {
5339  PerfLog = " 1 minute early";
5340  }
5341  else if(IntMinsLate > 1)
5342  {
5343  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5344  }
5345  else if(IntMinsLate < -1)
5346  {
5347  int PosIntMinsLate = -IntMinsLate;
5348  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5349  }
5350  if(LocationName.Pos('-') > 0)
5351  {
5352  PerfLog = "," + PerfLog;
5353  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5354  }
5355  Display->PerformanceLog(0, BaseLog + PerfLog);
5356  }
5357  else
5358  {
5359  Display->PerformanceLog(1, BaseLog);
5360  }
5361  if(Warning)
5362  {
5363  Display->WarningLog(0, WarningBaseLog);
5364  }
5365  // update statistics
5366  if((ActionType == Arrive) && (IntMinsLate == 0))
5367  {
5369  }
5370  else if((ActionType == Arrive) && (IntMinsLate > 0))
5371  {
5373  TrainController->TotLateArrMins += IntMinsLate;
5374  }
5375  else if((ActionType == Arrive) && (IntMinsLate < 0))
5376  {
5378  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5379  }
5380 
5381  else if((ActionType == Pass) && (IntMinsLate == 0))
5382  {
5384  }
5385  else if((ActionType == Pass) && (IntMinsLate > 0))
5386  {
5388  TrainController->TotLatePassMins += IntMinsLate;
5389  }
5390  else if((ActionType == Pass) && (IntMinsLate < 0))
5391  {
5393  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5394  }
5395 
5396  else if((ActionType == Leave) && (IntMinsLate == 0)) //new at v2.9.1 as had been omitted in error earlier
5397  {
5399  }
5400  else if((ActionType == Leave) && (IntMinsLate > 0))
5401  {
5403  TrainController->TotLateExitMins += IntMinsLate;
5404  }
5405  else if((ActionType == Leave) && (IntMinsLate < 0))
5406  {
5408  TrainController->TotEarlyExitMins += abs(IntMinsLate);
5409  }
5410 
5411  else if((ActionType == Depart) && (IntMinsLate == 0)) //can't depart early
5412  {
5414  }
5415  else if((ActionType == Depart) && (IntMinsLate > 0))
5416  {
5418  TrainController->TotLateDepMins += IntMinsLate;
5419  }
5420  Utilities->CallLogPop(968);
5421 }
5422 
5423 // ---------------------------------------------------------------------------
5424 
5425 void TTrain::TrainHasFailed(int Caller)
5426 {
5427  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5428  if(Crashed || Derailed || DerailPending)
5429  {
5430  TrainFailurePending = false;
5431  Utilities->CallLogPop(2135);
5432  return;
5433  }
5434  AnsiString LocName = "";
5435 
5436  if(LeadElement > -1)
5437  {
5439  }
5440  if((LocName == "") && (MidElement > -1))
5441  {
5443  }
5444  if((LocName == "") && LeadElement > -1)
5445  {
5446  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5447  }
5448  if((LocName == "") && (MidElement > -1))
5449  {
5450  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5451  }
5452  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5453  TrainFailed = true;
5454  TrainFailurePending = false;
5455  CallingOnFlag = false; //added at v2.10.0
5457  PowerAtRail = 0.08;
5458  AValue = sqrt(2 * PowerAtRail / Mass);
5460  // TrainFailed only called when PlotElements properly set to Lead, Mid & Lag elements
5461  if(Stopped())
5462  {
5463  EntrySpeed = 0;
5464  ExitSpeedHalf = 0;
5465  ExitSpeedFull = 0;
5466  MaxExitSpeed = 0;
5467  BrakeRate = 0;
5468  StoppedWithoutPower = true;
5469  }
5471  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5472  // true for warning, TDateTime not used
5473  Utilities->CallLogPop(2136);
5474 }
5475 
5476 // ---------------------------------------------------------------------------
5477 
5478 void TTrain::FrontTrainSplit(int Caller)
5479 {
5480 /*
5481  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5482  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5483  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5484  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5485 */
5486  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5487  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5488  if(PowerAtRail < 1)
5489  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5490  {
5492  {
5493  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5494  }
5496  Utilities->CallLogPop(2137);
5497  return;
5498  }
5499  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5500 
5501  if(LocationName == "")
5502  {
5503  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5504  }
5505  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5506  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5507  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5509 
5510  // determine all positions & exits
5511  if(LocationName != "")
5512  {
5513  // if message given only call at ~5 sec intervals
5515  {
5516  FirstNamedElementPos = LeadElement;
5517  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5518  // check if possible with LeadElement as First
5519  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5520  {
5521  FirstNamedElementPos = MidElement;
5522  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5523  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5524  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5525  {
5527  {
5528  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5530  }
5531  Utilities->CallLogPop(1009);
5532  return;
5533  }
5534  }
5535  else
5536  {
5537  // if first is possible then check if all 4 positions at location, and if not try the second
5538  int LeadPosA = FirstNamedElementPos;
5539  int LeadPosB = FirstNamedLinkedElementPos;
5540  int LeadPosC = SecondNamedElementPos;
5541  int LeadPosD = SecondNamedLinkedElementPos;
5542  // count number of positions that are at the location
5543  int LeadNumAtLoc = 0;
5544  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5545  {
5546  LeadNumAtLoc++;
5547  }
5548  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5549  {
5550  LeadNumAtLoc++;
5551  }
5552  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5553  {
5554  LeadNumAtLoc++;
5555  }
5556  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5557  {
5558  LeadNumAtLoc++;
5559  }
5560  if(LeadNumAtLoc < 4)
5561  {
5562  FirstNamedElementPos = MidElement;
5563  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5564  SecondNamedLinkedElementPos)) // restore originals
5565  {
5566  FirstNamedElementPos = LeadPosA;
5567  FirstNamedLinkedElementPos = LeadPosB;
5568  SecondNamedElementPos = LeadPosC;
5569  SecondNamedLinkedElementPos = LeadPosD;
5570  }
5571  else // count number at location
5572  {
5573  int MidNumAtLoc = 0;
5574  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5575  {
5576  MidNumAtLoc++;
5577  }
5578  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5579  {
5580  MidNumAtLoc++;
5581  }
5582  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5583  {
5584  MidNumAtLoc++;
5585  }
5586  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5587  {
5588  MidNumAtLoc++;
5589  }
5590  if(LeadNumAtLoc > MidNumAtLoc)
5591  // change back, else keep new values
5592  {
5593  FirstNamedElementPos = LeadPosA;
5594  FirstNamedLinkedElementPos = LeadPosB;
5595  SecondNamedElementPos = LeadPosC;
5596  SecondNamedLinkedElementPos = LeadPosD;
5597  }
5598  }
5599  }
5600  }
5601  }
5602  else
5603  {
5604  Utilities->CallLogPop(1791);
5605  return;
5606  }
5607  }
5608  else
5609  {
5610  throw Exception("Error - LocationName not set in FrontTrainSplit");
5611  }
5612  // set front & rear train parameters
5613  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5614  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5615  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5616  if(LeadElement == FirstNamedElementPos)
5617  {
5618  if(MidElement == SecondNamedElementPos)
5619  {
5620  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5621  FrontTrainRearPosition = FirstNamedElementPos;
5622  RearTrainFrontPosition = SecondNamedElementPos;
5623  RearTrainRearPosition = SecondNamedLinkedElementPos;
5624  }
5625  else // MidElement must == FirstNamedLinkedElementPos
5626  {
5627  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5628  FrontTrainRearPosition = SecondNamedElementPos;
5629  RearTrainFrontPosition = FirstNamedElementPos;
5630  RearTrainRearPosition = FirstNamedLinkedElementPos;
5631  }
5632  }
5633  else // MidElement == FirstNamedElementPos
5634  {
5635  if(LeadElement == SecondNamedElementPos)
5636  {
5637  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5638  FrontTrainRearPosition = SecondNamedElementPos;
5639  RearTrainFrontPosition = FirstNamedElementPos;
5640  RearTrainRearPosition = FirstNamedLinkedElementPos;
5641  }
5642  else // LeadElement must == FirstNamedLinkedElementPos
5643  {
5644  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5645  FrontTrainRearPosition = FirstNamedElementPos;
5646  RearTrainFrontPosition = SecondNamedElementPos;
5647  RearTrainRearPosition = SecondNamedLinkedElementPos;
5648  }
5649  }
5650  RearTrainExitPos = -1;
5651  for(int x = 0; x < 4; x++)
5652  {
5653  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5654  {
5655  RearTrainExitPos = x;
5656  break;
5657  }
5658  }
5659  if(RearTrainExitPos == -1)
5660  {
5661  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5662  }
5663  FrontTrainExitPos = -1;
5664  for(int x = 0; x < 4; x++)
5665  {
5666  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5667  {
5668  FrontTrainExitPos = x;
5669  break;
5670  }
5671  }
5672  if(FrontTrainExitPos == -1)
5673  {
5674  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5675  }
5676  // check no train (apart from self) on any of the 4 elements & fail if so
5677  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5678  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5679 
5680  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5681  {
5682  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5683  }
5684  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5685  {
5686  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5687  }
5688  else
5689  {
5690  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5691  }
5692  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5693  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5694  // can't be a bridge
5695  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5696  // can't be a bridge
5697  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5698  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5699 
5700  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5701  {
5702  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5703  }
5704  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5705  {
5706  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5707  }
5708  else
5709  {
5710  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5711  }
5712  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5713  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5714  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5715  {
5717  {
5720  }
5721  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5722  Utilities->CallLogPop(1010);
5723  return;
5724  }
5726  {
5728  }
5729  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5730  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5731  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5732  // variable as it is needed for setting up the new train
5733  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5734 
5735  UnplotTrain(0);
5736  StartSpeed = 0;
5737  RearStartElement = RearTrainRearPosition;
5738  RearStartExitPos = RearTrainExitPos;
5739  StoppedAtLocation = true;
5740  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5741  {
5742  StoppedWithoutPower = true;
5743  }
5744  PlotStartPosition(3);
5749 
5750  Mass = Mass / 2;
5751  MaxBrakeRate = MaxBrakeRate / 2;
5752  PowerAtRail = PowerAtRail / 2;
5753  AValue = sqrt(2 * PowerAtRail / Mass);
5754  // shouldn't change but include in case not set earlier
5755 
5756  // create new front train
5757 /*
5758  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5759  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5760  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5761 */
5762  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5763  TActionEventType EventType = NoEvent;
5764 
5765  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5766  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5767  // false for SignallerControl
5768  {
5769  Utilities->CallLogPop(1721); // EventType not used here
5770  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5771  // another train, in which case a message will have been sent to the perf log, also might well clear later
5772  // when other train moves away
5773  return;
5774  }
5775  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5776  // see mods in UpdateTrain for v1.3.2
5777  TrainController->TrainAdded = true;
5778 
5779  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5780 
5781  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5782  TTOD.RunningEntry = Running;
5783  Utilities->CallLogPop(998);
5784 }
5785 
5786 // ---------------------------------------------------------------------------
5787 
5788 void TTrain::RearTrainSplit(int Caller)
5789 {
5790 /*
5791  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5792  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5793  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5794  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5795 */
5796  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5797  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5798  if(PowerAtRail < 1)
5799  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5800  {
5802  {
5803  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
5804  }
5806  Utilities->CallLogPop(2138);
5807  return;
5808  }
5809  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
5810 
5811  if(LocationName == "")
5812  {
5813  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
5814  }
5815  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5816  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5817  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5819 
5820  // determine all positions & exits
5821  if(LocationName != "")
5822  {
5823  // if message given only call at ~5 sec intervals
5825  {
5826  FirstNamedElementPos = LeadElement;
5827  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5828  SecondNamedLinkedElementPos))
5829  {
5830  FirstNamedElementPos = MidElement;
5831  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5832  SecondNamedLinkedElementPos))
5833  {
5835  {
5836  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
5838  }
5839  Utilities->CallLogPop(1013);
5840  return;
5841  }
5842  }
5843  else
5844  {
5845  // if first is possible then check if all 4 positions at location, and if not try the second
5846  int LeadPosA = FirstNamedElementPos;
5847  int LeadPosB = FirstNamedLinkedElementPos;
5848  int LeadPosC = SecondNamedElementPos;
5849  int LeadPosD = SecondNamedLinkedElementPos;
5850  // count number of positions that are at the location
5851  int LeadNumAtLoc = 0;
5852  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
5853  {
5854  LeadNumAtLoc++;
5855  }
5856  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
5857  {
5858  LeadNumAtLoc++;
5859  }
5860  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
5861  {
5862  LeadNumAtLoc++;
5863  }
5864  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
5865  {
5866  LeadNumAtLoc++;
5867  }
5868  if(LeadNumAtLoc < 4)
5869  {
5870  FirstNamedElementPos = MidElement;
5871  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5872  SecondNamedLinkedElementPos)) // restore originals
5873  {
5874  FirstNamedElementPos = LeadPosA;
5875  FirstNamedLinkedElementPos = LeadPosB;
5876  SecondNamedElementPos = LeadPosC;
5877  SecondNamedLinkedElementPos = LeadPosD;
5878  }
5879  else // count number at location
5880  {
5881  int MidNumAtLoc = 0;
5882  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5883  {
5884  MidNumAtLoc++;
5885  }
5886  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5887  {
5888  MidNumAtLoc++;
5889  }
5890  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5891  {
5892  MidNumAtLoc++;
5893  }
5894  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5895  {
5896  MidNumAtLoc++;
5897  }
5898  if(LeadNumAtLoc > MidNumAtLoc)
5899  // change back, else keep new values
5900  {
5901  FirstNamedElementPos = LeadPosA;
5902  FirstNamedLinkedElementPos = LeadPosB;
5903  SecondNamedElementPos = LeadPosC;
5904  SecondNamedLinkedElementPos = LeadPosD;
5905  }
5906  }
5907  }
5908  }
5909  }
5910  else
5911  {
5912  Utilities->CallLogPop(1792);
5913  return;
5914  }
5915  }
5916  else
5917  {
5918  throw Exception("Error - LocationName not set in RearTrainSplit");
5919  }
5920  // set front & rear train parameters
5921  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5922  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5923  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5924  if(LeadElement == FirstNamedElementPos)
5925  {
5926  if(MidElement == SecondNamedElementPos)
5927  {
5928  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5929  FrontTrainRearPosition = FirstNamedElementPos;
5930  RearTrainFrontPosition = SecondNamedElementPos;
5931  RearTrainRearPosition = SecondNamedLinkedElementPos;
5932  }
5933  else // MidElement must == FirstNamedLinkedElementPos
5934  {
5935  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5936  FrontTrainRearPosition = SecondNamedElementPos;
5937  RearTrainFrontPosition = FirstNamedElementPos;
5938  RearTrainRearPosition = FirstNamedLinkedElementPos;
5939  }
5940  }
5941  else // MidElement == FirstNamedElementPos
5942  {
5943  if(LeadElement == SecondNamedElementPos)
5944  {
5945  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5946  FrontTrainRearPosition = SecondNamedElementPos;
5947  RearTrainFrontPosition = FirstNamedElementPos;
5948  RearTrainRearPosition = FirstNamedLinkedElementPos;
5949  }
5950  else // LeadElement must == FirstNamedLinkedElementPos
5951  {
5952  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5953  FrontTrainRearPosition = FirstNamedElementPos;
5954  RearTrainFrontPosition = SecondNamedElementPos;
5955  RearTrainRearPosition = SecondNamedLinkedElementPos;
5956  }
5957  }
5958  RearTrainExitPos = -1;
5959  for(int x = 0; x < 4; x++)
5960  {
5961  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5962  {
5963  RearTrainExitPos = x;
5964  break;
5965  }
5966  }
5967  if(RearTrainExitPos == -1)
5968  {
5969  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
5970  }
5971  FrontTrainExitPos = -1;
5972  for(int x = 0; x < 4; x++)
5973  {
5974  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5975  {
5976  FrontTrainExitPos = x;
5977  break;
5978  }
5979  }
5980  if(FrontTrainExitPos == -1)
5981  {
5982  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
5983  }
5984  // check no train (apart from self) on any of the 4 elements & fail if so
5985  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5986  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
5987 
5988  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5989  {
5990  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5991  }
5992  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5993  {
5994  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5995  }
5996  else
5997  {
5998  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5999  }
6000  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
6001  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
6002  // can't be a bridge
6003  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
6004  // can't be a bridge
6005  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
6006  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
6007 
6008  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
6009  {
6010  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
6011  }
6012  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
6013  {
6014  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
6015  }
6016  else
6017  {
6018  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
6019  }
6020  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
6021  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
6022  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
6023  {
6025  {
6028  }
6029  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
6030  Utilities->CallLogPop(1014);
6031  return;
6032  }
6034  {
6036  }
6037  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
6038  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
6039  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
6040  // variable as it is needed for setting up the new train
6041  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
6042 
6043  UnplotTrain(1);
6044  StartSpeed = 0;
6045  RearStartElement = FrontTrainRearPosition;
6046  RearStartExitPos = FrontTrainExitPos;
6047  StoppedAtLocation = true;
6048  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
6049  {
6050  StoppedWithoutPower = true;
6051  }
6052  PlotStartPosition(4);
6057  Mass = Mass / 2;
6058  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
6059  MaxBrakeRate = MaxBrakeRate / 2;
6060  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
6061  PowerAtRail = PowerAtRail / 2;
6062  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
6063  AValue = sqrt(2 * PowerAtRail / Mass);
6064  // shouldn't change but include in case not set earlier
6065 
6066  // create new rear train
6067 /*
6068  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
6069  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
6070  int RepeatNumber, int IncrementalMinutes)
6071 */
6072  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
6073  TActionEventType EventType = NoEvent;
6074 
6075  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
6076  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
6077  // false for SignallerControl
6078  {
6079  Utilities->CallLogPop(1722); // EventType not used here
6080  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
6081  // another train, in which case a message will have been sent to the perf log, also might well clear later
6082  // when other train moves away
6083  return;
6084  }
6085  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
6086  // see mods in UpdateTrain for v1.3.2
6087  TrainController->TrainAdded = true;
6088  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6089 
6090  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6091  TTOD.RunningEntry = Running;
6092  Utilities->CallLogPop(1015);
6093 }
6094 
6095 // ---------------------------------------------------------------------------
6096 
6097 void TTrain::FinishJoin(int Caller)
6098 {
6099  if(FinishJoinLogSent == false)
6100  {
6101  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6102  FinishJoinLogSent = true; // so don't keep logging this event
6103  // don't need to reset it to false after the event as the train is deleted
6104  }
6105  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6106  if(TrainFailed)
6107  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
6108  {
6110  {
6111  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6112  }
6114  Utilities->CallLogPop(2139);
6115  return;
6116  }
6117  if(TrainGone)
6118  // this means that the train has already joined the other & is awaiting deletion by TrainController
6119  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6120  // on from jbo & TrainToJoinIsAdjacent returns false
6121  {
6122  Utilities->CallLogPop(1035);
6123  return;
6124  }
6125  TTrain *TrainToJoin;
6127 
6128  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6129  {
6131  {
6132  // Display->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6135  }
6136  Utilities->CallLogPop(1030);
6137  return; // keep this here in case need to add code before final return
6138  }
6139  // no need to clear error report flag here, cleared in jbo function
6140  // No need to set TimetableFinished, done in jbo function
6141  Utilities->CallLogPop(1031);
6142 }
6143 
6144 // ---------------------------------------------------------------------------
6145 
6146 void TTrain::JoinedBy(int Caller)
6147 {
6148  TrainController->LogEvent("" + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6149  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6150  if(PowerAtRail < 1)
6151  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6152  {
6154  {
6155  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6156  }
6158  Utilities->CallLogPop(2140);
6159  return;
6160  }
6161  TTrain *TrainToBeJoinedBy;
6163 
6164  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6165  {
6167  {
6168  // Display->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6171  }
6172  LastActionDelayFlag = true;
6173  // need to update LastActionTime if this train first to arrive as need 30s after
6174  // both adjacent before the join
6175  Utilities->CallLogPop(1032);
6176  return;
6177  }
6178  // here when other train is adjacent
6180  {
6182  // need to update this as need 30s after both adjacent before the join
6183  LastActionDelayFlag = false;
6184  Utilities->CallLogPop(1033);
6185  return;
6186  }
6187  // here when other train is adjacent & 30 secs elapsed since both adjacent
6188 
6189  // set new values for mass etc
6190  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6191  {
6192  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6193  }
6194  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6195  double OwnBrakeForce = MaxBrakeRate * Mass;
6196  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6197 
6198  Mass += TrainToBeJoinedBy->Mass;
6199  MaxBrakeRate = CombinedBrakeRate;
6200  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6201  AValue = sqrt(2 * PowerAtRail / Mass);
6202 
6204  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6205  TrainToBeJoinedBy->TimetableFinished = true;
6206  TrainToBeJoinedBy->TrainGone = true;
6207  // this will cause other train to be deleted
6208  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6212  Utilities->CallLogPop(1034);
6213 }
6214 
6215 // ---------------------------------------------------------------------------
6216 
6218 {
6219  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6220  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6221  if(PowerAtRail < 1)
6222  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6223  {
6225  {
6226  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6227  }
6228  ZeroPowerNoCDTMessage = true;
6229  Utilities->CallLogPop(2141);
6230  return;
6231  }
6232  TColor TempColour = BackgroundColour;
6233 
6234  UnplotTrain(2);
6237  StartSpeed = 0;
6238  StoppedAtLocation = true;
6239  PlotStartPosition(1);
6240  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6241  // plot same as was - should always be pale green
6245 
6246  //now erase a stub route if there is one, added at v2.5.1
6247  //first element of route is now immediately behind the train (i.e. next to MidElement)
6248  if(MidEntryPos >= 0)
6249  {
6250  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6251  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6252  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6253  int RouteNumber = -1;
6254  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6255  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6256  {
6257  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6258  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6259  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6260  {
6261  bool FirstPass = true; //added at v2.8.0
6262  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6263  {
6264  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6265  int TVPos2 = PDE.GetTrackVectorPosition();
6266  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6267  {
6268  break;
6269  }
6270  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6272  {
6273  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6274  }
6275  else
6276  {
6277  break;
6278  }
6279  FirstPass = false;
6280  }
6281  AllRoutes->RebuildRailwayFlag = true;
6282  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6283  }
6284  }
6285  }
6286  Utilities->CallLogPop(1012);
6287 }
6288 
6289 // ---------------------------------------------------------------------------
6290 
6291 void TTrain::NewTrainService(int Caller)
6292 // change to new train, give new service message
6293 {
6294  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6295  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6296  if(PowerAtRail < 1)
6297  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6298  {
6300  {
6301  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6302  }
6304  Utilities->CallLogPop(2142);
6305  return;
6306  }
6308 
6310  UnplotTrain(3);
6313  StartSpeed = 0;
6318  HeadCode = NewHeadCode;
6319  StoppedAtLocation = true;
6320  PlotStartPosition(5);
6322  // pale green
6325  TerminatedMessageSent = false;
6326  Utilities->CallLogPop(1022);
6327 }
6328 
6329 // ---------------------------------------------------------------------------
6330 
6331 void TTrain::RemainHere(int Caller)
6332 {
6333  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6334  if(RemainHereLogNotSent) // to prevent repeated logs
6335  {
6336  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6337  RemainHereLogNotSent = false;
6338  }
6340  {
6341  Display->PerformanceLog(5, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " terminated at " +
6344  TerminatedMessageSent = true;
6345  }
6346  TimetableFinished = true;
6347  Utilities->CallLogPop(1023);
6348 }
6349 
6350 // ---------------------------------------------------------------------------
6351 
6352 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6353 /*
6354  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6355  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6356  except where an action is a departure, starting at the current value for the pointer
6357  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6358  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6359 */{
6360  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6361  {
6362  return; // if remove train that starts under signaller control no messages needed
6363 
6364  }
6365  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6366  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6367  if(IncNum > 0)
6368  {
6369  for(int x = 0; x < IncNum; x++)
6370  {
6371  if(x > 0)
6372  {
6373  Ptr++;
6374  }
6375  // arrival - no need to test for termination as this section only covers missed actions up to the
6376  // arrival point - may terminate later but that not missed
6377  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6378  {
6380  }
6381  // arrival & departure
6382  if(Ptr->FormatType == TimeTimeLoc)
6383  {
6385  }
6386  // departure
6387  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6388  {
6389  continue; // skip TimeLoc departures, message given for arrivals
6390  }
6391  // pass
6392  else if(Ptr->FormatType == PassTime)
6393  {
6395  }
6396  // split
6397  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6398  {
6400  }
6401  // jbo
6402  else if(Ptr->Command == "jbo")
6403  {
6405  }
6406  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6407  // be starts, finishes or cdt
6408  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6409  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6410  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6411  (Ptr->FormatType == Repeat))
6412  {
6413  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6414  }
6415  }
6416  }
6417  else
6418  {
6419  bool IncludeFER = false;
6420  if(IncNum == -1)
6421  {
6422  IncludeFER = true;
6423  }
6424  while(true) // finish commands & repeats break out of loop
6425  {
6426  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6427  if(!IncludeFER && (Ptr->Command == "Fer"))
6428  {
6429  break;
6430  }
6431  // Fer & included
6432  else if(IncludeFER && (Ptr->Command == "Fer"))
6433  {
6435  break;
6436  }
6437  // Repeat
6438  else if(Ptr->FormatType == Repeat)
6439  {
6440  break;
6441  }
6442  // Fjo
6443  else if(Ptr->Command == "Fjo")
6444  {
6446  break;
6447  }
6448  // Frh
6449  else if(Ptr->Command == "Frh")
6450  {
6452  {
6454  TerminatedMessageSent = true;
6455  }
6456  break;
6457  }
6458  // Frh-sh
6459  else if(Ptr->Command == "Frh-sh")
6460  {
6462  {
6464  TerminatedMessageSent = true;
6465  }
6466  break;
6467  }
6468  // Fns, F-nshs, Fns-sh
6469  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6470  {
6472  break;
6473  }
6474  // end of breakout actions
6475  // arrival
6476  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6477  {
6478  if(IsTrainTerminating(1))
6479  {
6481  TerminatedMessageSent = true;
6482  }
6483  else
6484  {
6486  }
6487  }
6488  // arrival & departure
6489  else if(Ptr->FormatType == TimeTimeLoc)
6490  {
6492  }
6493  // departure
6494  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6495  {
6496  Ptr++;
6497  continue; // skip TimeLoc departures, message given for arrivals
6498  }
6499  // pass
6500  else if(Ptr->FormatType == PassTime)
6501  {
6503  }
6504  // split
6505  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6506  {
6508  }
6509  // jbo
6510  else if(Ptr->Command == "jbo")
6511  {
6513  }
6514  // cdt
6515  else if(Ptr->Command == "cdt")
6516  {
6518  }
6519  // Errors
6520  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6521  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6522  {
6523  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6524  }
6525  Ptr++;
6526  }
6527  TimetableFinished = true;
6528  }
6529  Utilities->CallLogPop(1021);
6530 }
6531 
6532 // ---------------------------------------------------------------------------
6533 
6534 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6535 // ensure same repeatnumber
6536 {
6537  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6539 
6540  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6541  {
6542  Utilities->CallLogPop(1024);
6543  return(false);
6544  }
6545  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6546  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6547  {
6548  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6549  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6550  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6551  {
6552  Utilities->CallLogPop(1025);
6553  return(true);
6554  }
6555  }
6556  Utilities->CallLogPop(1026);
6557  return(false);
6558 }
6559 
6560 // ---------------------------------------------------------------------------
6561 
6562 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6563 // ensure same repeatnumber
6564 {
6565  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6566  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6567 
6568  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6569  {
6570  Utilities->CallLogPop(1027);
6571  return(false);
6572  }
6573  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6574  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6575  {
6576  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6577  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6578  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6579  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6580  {
6581  Utilities->CallLogPop(1028);
6582  return(true);
6583  }
6584  }
6585  Utilities->CallLogPop(1029);
6586  return(false);
6587 }
6588 
6589 // ---------------------------------------------------------------------------
6590 
6592 {
6593  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6594  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6595  if(PowerAtRail < 1)
6596  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6597  {
6599  {
6600  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6601  }
6603  Utilities->CallLogPop(2143);
6604  return;
6605  }
6606  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6607 
6609  UnplotTrain(4);
6612  StartSpeed = 0;
6617  HeadCode = NewHeadCode;
6618  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6619  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6620  StoppedAtLocation = true;
6621  PlotStartPosition(6);
6623  // pale green
6626  TerminatedMessageSent = false;
6627  Utilities->CallLogPop(1078);
6628 }
6629 
6630 // ---------------------------------------------------------------------------
6631 
6633 // need to check whether all repeats finished or not
6634 {
6635  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6636  if(RemainHereLogNotSent) // to prevent repeated logs
6637  {
6638  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6639  RemainHereLogNotSent = false;
6640  }
6642  // finished all repeats
6643  {
6645  {
6648  TerminatedMessageSent = true;
6649  // no need to clear message as no more actions
6650  }
6651  TimetableFinished = true;
6652  Utilities->CallLogPop(1080);
6653  return;
6654  }
6655  if(PowerAtRail < 1)
6656  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6657  {
6659  {
6660  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6661  }
6663  Utilities->CallLogPop(2144);
6664  return;
6665  }
6666  int TempRepeatNumber = RepeatNumber + 1;
6667  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6668  // until after LogAction or the wrong time will be used
6669  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6670 
6672  RepeatNumber++;
6673  UnplotTrain(5);
6676  StartSpeed = 0;
6681  HeadCode = NewHeadCode;
6682  StoppedAtLocation = true;
6683  PlotStartPosition(7);
6685  // pale green
6688  TerminatedMessageSent = false;
6689  Utilities->CallLogPop(1079);
6690 }
6691 
6692 // ---------------------------------------------------------------------------
6693 
6695 {
6696  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6697  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6698  if(PowerAtRail < 1)
6699  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6700  {
6702  {
6703  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6704  }
6706  Utilities->CallLogPop(2145);
6707  return;
6708  }
6710  // finished all repeats
6711  {
6712  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6714  RepeatNumber = 0;
6715  UnplotTrain(6);
6718  StartSpeed = 0;
6720  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6721  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6723  HeadCode = NewHeadCode;
6724  StoppedAtLocation = true;
6725  PlotStartPosition(9);
6729  TerminatedMessageSent = false;
6730  Utilities->CallLogPop(1081);
6731  return;
6732  }
6733  int TempRepeatNumber = RepeatNumber + 1;
6734  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6735  // until after LogAction or the wrong time will be used
6736  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6737 
6739  RepeatNumber++;
6740  UnplotTrain(7);
6743  StartSpeed = 0;
6748  HeadCode = NewHeadCode;
6749  StoppedAtLocation = true;
6750  PlotStartPosition(8);
6752  // pale green
6755  TerminatedMessageSent = false;
6756  Utilities->CallLogPop(1082);
6757 }
6758 
6759 // ---------------------------------------------------------------------------
6760 
6762 {
6763  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6764  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6765  // must be preceded by a TimeLoc departure
6766  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6767  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6768  {
6770  {
6771  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6772  {
6773  Utilities->CallLogPop(1083);
6774  return(false);
6775  }
6776  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
6777  {
6778  Utilities->CallLogPop(1084);
6779  return(true);
6780  }
6781  }
6782  }
6783  Utilities->CallLogPop(1085);
6784  return(false);
6785 }
6786 
6787 // ---------------------------------------------------------------------------
6788 
6789 bool TTrain::AbleToMove(int Caller)
6790 {
6791  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6792  bool Able = true;
6793 
6794  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0 &
6795  {
6796  // StoppedForTrainInFront removed as tested below
6797  Able = false;
6798  Utilities->CallLogPop(2146); // added v2.4.0
6799  return(Able); // added v2.4.0
6800  }
6801  if(LeadElement > -1)
6802  {
6803  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6804  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6805  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront)
6806  {
6807  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6808  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6809  {
6810  Able = true;
6811  StoppedForTrainInFront = false;
6812  }
6813  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeTrackPos01 == -1))
6814  {
6815  Able = true;
6816  StoppedForTrainInFront = false;
6817  }
6818  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeTrackPos23 == -1))
6819  {
6820  Able = true;
6821  StoppedForTrainInFront = false;
6822  }
6823  }
6824  else
6825  {
6827  {
6828  Able = false;
6829  }
6830  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6831  }
6832  }
6833  else // leaving at a continuation so keep going
6834  {
6835  Able = true;
6836  StoppedForTrainInFront = false;
6837  }
6838  Utilities->CallLogPop(1454);
6839  return(Able);
6840 }
6841 
6842 // ---------------------------------------------------------------------------
6843 
6845 {
6846  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
6847  // won't be set; if there is a train then set StoppedForTrainInFront
6848  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
6849  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
6850  if(LeadElement == -1) // exiting at continuation
6851  {
6852  Utilities->CallLogPop(2045);
6853  return(false);
6854  }
6855  // end of addition
6856  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
6857  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
6858 
6859  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
6860  {
6861  StoppedForTrainInFront = true;
6862  Utilities->CallLogPop(1455);
6863  return(false);
6864  }
6865  else
6866  {
6867  Utilities->CallLogPop(1456);
6869  // StoppedWithoutPower added v2.4.0
6870  }
6871 }
6872 
6873 // ---------------------------------------------------------------------------
6874 
6876 {
6877  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
6878  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
6879  TColor TempColour = BackgroundColour;
6880 
6881  UnplotTrain(8);
6884  StartSpeed = 0;
6885  PlotStartPosition(2);
6886  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
6887 
6888  //now erase a stub route if there is one, added at v2.5.1
6889  //first element of route is now immediately behind the train (i.e. next to MidElement)
6890  if(MidEntryPos >= 0)
6891  {
6892  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
6893  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6894  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6895  int RouteNumber = -1;
6896  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6897  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6898  {
6899  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
6900  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
6901  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6902  {
6903  bool FirstPass = true; //added at v2.8.0
6904  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a route extends to another signal
6905  {
6906  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
6907  int TVPos2 = PDE.GetTrackVectorPosition();
6908  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6909  {
6910  break;
6911  }
6912  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
6914  {
6915  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6916  }
6917  else
6918  {
6919  break;
6920  }
6921  FirstPass = false;
6922  }
6923  AllRoutes->RebuildRailwayFlag = true;
6924  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6925  }
6926  }
6927  }
6928  Utilities->CallLogPop(1102);
6929 }
6930 
6931 // ---------------------------------------------------------------------------
6932 
6934 {
6935  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6936  ",FloatingLabelNextString" + "," + HeadCode);
6937  AnsiString RetStr = "", LocationName = "";
6938 
6939  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
6940  {
6941  throw Exception("Error - start entry in FloatingLabelNextString");
6942  }
6943  if(Ptr->FormatType == TimeTimeLoc)
6944  {
6945  if(TrainMode == Timetable)
6946  {
6947  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
6948  // not arrived yet in tt mode
6949  {
6950  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime));
6951  }
6952  else
6953  {
6954  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(3, Ptr->DepartureTime));
6955  }
6956  }
6957  else // TrainMode == Signaller
6958  {
6959  if(!DepartureTimeSet) // not arrived yet
6960  {
6961  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime));
6962  }
6963  else
6964  {
6965  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(36, Ptr->DepartureTime));
6966  }
6967  }
6968  }
6969  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
6970  {
6971  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime));
6972  }
6973  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6974  {
6975  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(5, Ptr->DepartureTime));
6976  }
6977  else if(Ptr->FormatType == PassTime) // new
6978  {
6979  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime));
6980  }
6981  else if(Ptr->Command == "Fns")
6982  {
6983  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
6984  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime));
6985  RetStr = CheckNewServiceDepartureTime(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6986  }
6987  else if(Ptr->Command == "F-nshs")
6988  {
6989  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
6991  RetStr = CheckNewServiceDepartureTime(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6992  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
6993  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
6994  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
6995  }
6996  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6997  {
6998  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6999  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime));
7000  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7001  RetStr = CheckNewServiceDepartureTime(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7002  }
7003  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7004  {
7005  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7006  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime));
7007  RetStr = CheckNewServiceDepartureTime(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7008  }
7009  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7010  {
7011  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7012  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime));
7013  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7014  RetStr = CheckNewServiceDepartureTime(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7015  }
7016  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7017  {
7018  RetStr ="None, train terminated at " + Ptr->LocationName;
7019  }
7020  else if(Ptr->Command == "Frh")
7021  {
7022  RetStr = "None, train terminated at " + Ptr->LocationName;
7023  }
7024  else if(Ptr->Command == "Fer")
7025  {
7026  AnsiString AllowedExits = "";
7027  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime)) + AllowedExits;
7028  }
7029  else if(Ptr->Command == "Fjo")
7030  {
7031  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
7033  }
7034  else if(Ptr->Command == "jbo")
7035  {
7036  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7037  " at " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime));
7038  }
7039  else if(Ptr->Command == "fsp")
7040  {
7041  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7042  " at " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime));
7043  }
7044  else if(Ptr->Command == "rsp")
7045  {
7046  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7047  " at " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime));
7048  }
7049  else if(Ptr->Command == "cdt")
7050  {
7051  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime));
7052  }
7053  Utilities->CallLogPop(1124);
7054  return(RetStr);
7055 }
7056 
7057 // ---------------------------------------------------------------------------
7058 
7059 AnsiString TTrain::CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
7060 {
7061  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
7062  + AnsiString(RptNum) + ",CheckNewServiceDepartureTime," + HeadCode);
7063  AnsiString DepTime = "", EventTime = "";
7064  bool CDTFlag = false; //reports if train changes direction before departs
7065  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
7066  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
7067  {
7068  if(AVI->Command == "cdt")
7069  {
7070  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
7071  continue;
7072  }
7073  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
7074  {
7075  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes));
7076  RetStr += "\nNew service splits at " + EventTime;
7077  Utilities->CallLogPop(2234);
7078  return(RetStr);
7079  }
7080  if(AVI->Command == "jbo")
7081  {
7082  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes));
7083  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
7084  Utilities->CallLogPop(2235);
7085  return(RetStr);
7086  }
7087  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
7088  {
7089  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
7090  if(CDTFlag)
7091  {
7092  RetStr += "\nNew service changes direction then departs at " + DepTime;
7093  }
7094  else
7095  {
7096  RetStr += "\nNew service departs at " + DepTime;
7097  }
7098  Utilities->CallLogPop(2236);
7099  return(RetStr);
7100  }
7101  }
7102  Utilities->CallLogPop(2208);
7103  return(RetStr); //if reach here then RetStr doesn't change
7104 }
7105 
7106 // ---------------------------------------------------------------------------
7107 
7109 // Enter with Ptr pointing to first action to be listed (i.e. next action)
7110 // If there are actions to be skipped but a departure is awaited (SkippedDeparture = true) then after the departure Ptr moves forward by SkipPtrValue
7111 {
7112  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7113  ",FloatingTimetableString" + "," + HeadCode);
7114  AnsiString RetStr = "", PartStr = "";
7115  int Count = 0;
7116  bool SkipDep = false, SkipDepActedOn = false; //SkipDepActedOn ensures only one SkipDep acted on
7117  AnsiString LocName = Ptr->LocationName;
7118 
7119  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
7120  // can start in signaller control so exclude this
7121  {
7122  throw Exception("Error - start entry in FloatingTimetableString");
7123  }
7124  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTimeLoc check later
7125  bool FirstPass = true;
7126  Ptr--; // because incremented at start of loop
7127 
7128  // different first TimeTimeLoc display if in signaller control
7129  do
7130  {
7131  Ptr++;
7132  if((Ptr->FormatType == Repeat) || TimetableFinished)
7133  {
7134  break;
7135  }
7136  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
7137  {
7138  AnsiString TrainLoc = "";
7139  if(TrainMode == Timetable)
7140  {
7141  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7142  {
7143  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7144  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7145  {
7146  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7147  }
7148  }
7149  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7150  {
7151  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7152  }
7153  else
7154  {
7155  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7156  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7157  Count++; // because there are 2 entries
7158  }
7159  }
7160  else // TrainMode == Signaller
7161  {
7162  if(DepartureTimeSet)
7163  {
7164  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7165  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7166  {
7167  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7168  }
7169  }
7170  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7171  {
7172  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7173  }
7174  else
7175  {
7176  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7177  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7178  Count++; // because there are 2 entries
7179  }
7180  }
7181  }
7182  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
7183  {
7184  AnsiString TrainLoc = "";
7185  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7186  {
7187  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7188  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7189  {
7190  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7191  }
7192  }
7193  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7194  {
7195  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7196  }
7197  else
7198  {
7199  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7200  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7201  Count++; // because there are 2 entries
7202  }
7203  }
7204  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7205  {
7206  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
7207  }
7208  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7209  {
7210  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7211  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7212  {
7213  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7214  }
7215  }
7216  else if(Ptr->FormatType == PassTime) // new
7217  {
7218  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
7219  }
7220  else if(Ptr->Command == "Fns")
7221  {
7222  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
7223  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7224  PartStr = CheckNewServiceDepartureTime(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to PartStr
7225  }
7226  else if(Ptr->Command == "F-nshs")
7227  {
7228  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
7229  Ptr->LocationName;
7230  PartStr = CheckNewServiceDepartureTime(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7231  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7232  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7233  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7234  }
7235  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7236  {
7237  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
7238  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7239  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7240  PartStr = CheckNewServiceDepartureTime(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7241  }
7242  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7243  {
7244  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7245  +" at " + Ptr->LocationName;
7246  PartStr = CheckNewServiceDepartureTime(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7247  }
7248  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7249  {
7250  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
7251  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7252  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7253  PartStr = CheckNewServiceDepartureTime(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7254  }
7255  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7256  {
7257  PartStr = "Terminate at " + Ptr->LocationName;
7258  }
7259  else if(Ptr->Command == "Frh")
7260  {
7261  PartStr = "Terminate at " + Ptr->LocationName;
7262  }
7263  else if(Ptr->Command == "Fer")
7264  {
7265  AnsiString AllowedExits = "";
7266  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
7267  }
7268  else if(Ptr->Command == "Fjo")
7269  {
7270  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
7271  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7272  }
7273  else if(Ptr->Command == "jbo")
7274  {
7275  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
7276  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7277  }
7278  else if(Ptr->Command == "fsp")
7279  {
7280  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
7281  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7282  }
7283  else if(Ptr->Command == "rsp")
7284  {
7285  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
7286  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7287  }
7288  else if(Ptr->Command == "cdt")
7289  {
7290  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
7291  }
7292  if(RetStr != "")
7293  {
7294  RetStr = RetStr + '\n' + PartStr;
7295  }
7296  else
7297  {
7298  RetStr = PartStr;
7299  }
7300  FirstPass = false;
7301  Count++;
7302 
7303  if(SkipDep)
7304  {
7305  Ptr = &(TrainDataEntryPtr->ActionVector.at(0)) + SkipPtrValue;
7306  Ptr--; //it is incremented at the start of the next loop
7307  SkipDep = false;
7308  SkipDepActedOn = true;
7309  }
7310  }
7311  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
7312  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
7313  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
7314  // forward as anyone should wish to see without looking at the full timetable
7315  if(TimetableFinished)
7316  {
7317  if(TrainMode == Timetable)
7318  {
7319  RetStr = "Timetable finished";
7320  }
7321  else
7322  {
7323  RetStr = "No timetable";
7324  }
7325  }
7326  Utilities->CallLogPop(1125);
7327  return(RetStr);
7328 }
7329 
7330 // ---------------------------------------------------------------------------
7331 
7332 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
7333 {
7334  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
7335  Utilities->SaveFileString(OutFile, HeadCode);
7338  Utilities->SaveFileInt(OutFile, StartSpeed);
7341  Utilities->SaveFileInt(OutFile, RepeatNumber);
7344  Utilities->SaveFileInt(OutFile, Mass);
7347  Utilities->SaveFileDouble(OutFile, EntrySpeed);
7354  Utilities->SaveFileDouble(OutFile, BrakeRate);
7358  Utilities->SaveFileDouble(OutFile, double(EntryTime));
7359  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
7360  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
7361  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
7362  Utilities->SaveFileDouble(OutFile, double(TRSTime));
7363  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
7367  Utilities->SaveFileInt(OutFile, (short)TrainMode);
7372  Utilities->SaveFileBool(OutFile, Derailed);
7374  Utilities->SaveFileBool(OutFile, Crashed);
7381  Utilities->SaveFileBool(OutFile, NotInService);
7382  Utilities->SaveFileBool(OutFile, Plotted);
7383  Utilities->SaveFileBool(OutFile, TrainGone);
7384  Utilities->SaveFileBool(OutFile, SPADFlag);
7386  Utilities->SaveFileInt(OutFile, HOffset[0]);
7387  Utilities->SaveFileInt(OutFile, HOffset[1]);
7388  Utilities->SaveFileInt(OutFile, HOffset[2]);
7389  Utilities->SaveFileInt(OutFile, HOffset[3]);
7390  Utilities->SaveFileInt(OutFile, VOffset[0]);
7391  Utilities->SaveFileInt(OutFile, VOffset[1]);
7392  Utilities->SaveFileInt(OutFile, VOffset[2]);
7393  Utilities->SaveFileInt(OutFile, VOffset[3]);
7394  Utilities->SaveFileInt(OutFile, PlotElement[0]);
7395  Utilities->SaveFileInt(OutFile, PlotElement[1]);
7396  Utilities->SaveFileInt(OutFile, PlotElement[2]);
7397  Utilities->SaveFileInt(OutFile, PlotElement[3]);
7398  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
7399  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
7400  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
7401  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
7403  Utilities->SaveFileInt(OutFile, (short)Straddle);
7404  Utilities->SaveFileInt(OutFile, NextTrainID);
7405  Utilities->SaveFileInt(OutFile, TrainID);
7406  Utilities->SaveFileInt(OutFile, LeadElement);
7407  Utilities->SaveFileInt(OutFile, LeadEntryPos);
7408  Utilities->SaveFileInt(OutFile, LeadExitPos);
7409  Utilities->SaveFileInt(OutFile, MidElement);
7410  Utilities->SaveFileInt(OutFile, MidEntryPos);
7411  Utilities->SaveFileInt(OutFile, MidExitPos);
7412  Utilities->SaveFileInt(OutFile, LagElement);
7413  Utilities->SaveFileInt(OutFile, LagEntryPos);
7414  Utilities->SaveFileInt(OutFile, LagExitPos);
7415  int ColourNumber;
7416 
7418  {
7419  ColourNumber = 0;
7420  }
7422  {
7423  ColourNumber = 1;
7424  }
7426  {
7427  ColourNumber = 2;
7428  }
7430  {
7431  ColourNumber = 3;
7432  }
7434  {
7435  ColourNumber = 4;
7436  }
7438  {
7439  ColourNumber = 5;
7440  }
7442  {
7443  ColourNumber = 6;
7444  }
7446  {
7447  ColourNumber = 7;
7448  }
7450  {
7451  ColourNumber = 8;
7452  }
7454  {
7455  ColourNumber = 9;
7456  }
7458  {
7459  ColourNumber = 10;
7460  }
7462  {
7463  ColourNumber = 11;
7464  }
7466  {
7467  ColourNumber = 12;
7468  }
7469  else if(BackgroundColour == clTRSBackground)
7470  {
7471  ColourNumber = 13;
7472  }
7474  {
7475  ColourNumber = 14; // added at v2.4.0
7476  }
7477  Utilities->SaveFileInt(OutFile, ColourNumber);
7478 
7479  // additional data
7480  bool ForwardHeadCode;
7481 
7482  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
7483  {
7484  ForwardHeadCode = true;
7485  }
7486  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
7487  else
7488  {
7489  ForwardHeadCode = false;
7490  }
7491  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
7492 
7493  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
7494 
7495  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
7496  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
7497 
7498  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
7499  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
7500  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
7501  // so use the last asterisk position for this - 0 for false & 1 for true
7502  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
7503  AnsiString Marker;
7504 
7506  {
7507  Marker = "*****1";
7508  }
7509  else
7510  {
7511  Marker = "*****0";
7512  }
7513  if(RestoreTimetableLocation == "")
7514  {
7515  Utilities->SaveFileString(OutFile, Marker);
7516  }
7517  else
7518  {
7519  AnsiString CombinedString = RestoreTimetableLocation + Marker;
7520  Utilities->SaveFileString(OutFile, CombinedString);
7521  // RestoreTimetableLocation + marker
7522  }
7523  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
7524  Utilities->CallLogPop(1457);
7525 }
7526 
7527 // ---------------------------------------------------------------------------
7528 
7529 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
7530 {
7531  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
7532  HeadCode = Utilities->LoadFileString(InFile);
7535  StartSpeed = Utilities->LoadFileInt(InFile);
7537  if(SignallerMaxSpeed < 10)
7538  {
7539  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
7540  }
7542  RepeatNumber = Utilities->LoadFileInt(InFile);
7545  Mass = Utilities->LoadFileInt(InFile);
7548  {
7550  }
7551  // above added at v2.1.0 for legacy session files where value may not have been limited
7553  EntrySpeed = Utilities->LoadFileDouble(InFile);
7557  if(TimetableMaxRunningSpeed < 10)
7558  {
7559  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7560  }
7562  if(MaxRunningSpeed < 10)
7563  {
7564  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7565  }
7568  BrakeRate = Utilities->LoadFileDouble(InFile);
7572  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
7573  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
7574  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
7575  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
7576  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
7577  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
7586  Derailed = Utilities->LoadFileBool(InFile);
7588  Crashed = Utilities->LoadFileBool(InFile);
7595  NotInService = Utilities->LoadFileBool(InFile);
7596  Plotted = Utilities->LoadFileBool(InFile);
7597  TrainGone = Utilities->LoadFileBool(InFile);
7598  SPADFlag = Utilities->LoadFileBool(InFile);
7600  HOffset[0] = Utilities->LoadFileInt(InFile);
7601  HOffset[1] = Utilities->LoadFileInt(InFile);
7602  HOffset[2] = Utilities->LoadFileInt(InFile);
7603  HOffset[3] = Utilities->LoadFileInt(InFile);
7604  VOffset[0] = Utilities->LoadFileInt(InFile);
7605  VOffset[1] = Utilities->LoadFileInt(InFile);
7606  VOffset[2] = Utilities->LoadFileInt(InFile);
7607  VOffset[3] = Utilities->LoadFileInt(InFile);
7608  PlotElement[0] = Utilities->LoadFileInt(InFile);
7609  PlotElement[1] = Utilities->LoadFileInt(InFile);
7610  PlotElement[2] = Utilities->LoadFileInt(InFile);
7611  PlotElement[3] = Utilities->LoadFileInt(InFile);
7612  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
7613  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
7614  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
7615  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
7617  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
7618  NextTrainID = Utilities->LoadFileInt(InFile);
7619  // will be same for all but best to save all anyway
7620  TrainID = Utilities->LoadFileInt(InFile);
7621  LeadElement = Utilities->LoadFileInt(InFile);
7622  LeadEntryPos = Utilities->LoadFileInt(InFile);
7623  LeadExitPos = Utilities->LoadFileInt(InFile);
7624  MidElement = Utilities->LoadFileInt(InFile);
7625  MidEntryPos = Utilities->LoadFileInt(InFile);
7626  MidExitPos = Utilities->LoadFileInt(InFile);
7627  LagElement = Utilities->LoadFileInt(InFile);
7628  LagEntryPos = Utilities->LoadFileInt(InFile);
7629  LagExitPos = Utilities->LoadFileInt(InFile);
7630  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
7631 
7632  if(ColourNumber == 0)
7633  {
7635  }
7636  else if(ColourNumber == 1)
7637  {
7639  }
7640  else if(ColourNumber == 2)
7641  {
7643  }
7644  else if(ColourNumber == 3)
7645  {
7647  }
7648  else if(ColourNumber == 4)
7649  {
7651  }
7652  else if(ColourNumber == 5)
7653  {
7655  }
7656  else if(ColourNumber == 6)
7657  {
7659  }
7660  else if(ColourNumber == 7)
7661  {
7663  }
7664  else if(ColourNumber == 8)
7665  {
7667  }
7668  else if(ColourNumber == 9)
7669  {
7671  }
7672  else if(ColourNumber == 10)
7673  {
7675  }
7676  else if(ColourNumber == 11)
7677  {
7679  }
7680  else if(ColourNumber == 12)
7681  {
7683  }
7684  else if(ColourNumber == 13)
7685  {
7687  }
7688  else if(ColourNumber == 14)
7689  {
7690  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
7691 
7692  }
7693  // additional data
7695  // sets the BackgroundColour to the loaded value
7696  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
7697 
7698  if(ForwardHeadCode)
7699  {
7700  for(int x = 0; x < 4; x++)
7701  {
7703  }
7704  }
7705  else
7706  {
7707  for(int x = 0; x < 4; x++)
7708  {
7709  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
7710  }
7711  }
7712  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
7713  if(TrainMode == Timetable)
7714  {
7715  if(Crashed)
7716  {
7718  }
7719  else
7720  {
7722  }
7723  }
7724  else
7725  {
7727  }
7729  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
7730  if(Straddle == LeadMid)
7731  {
7732  if(LeadElement > -1)
7733  {
7735  }
7736  if(LeadElement > -1)
7737  {
7739  }
7740  if(MidElement > -1)
7741  {
7743  }
7744  if(MidElement > -1)
7745  {
7747  }
7748  }
7749  else if(Straddle == LeadMidLag)
7750  {
7751  if(LeadElement > -1)
7752  {
7754  }
7755  if(MidElement > -1)
7756  {
7758  }
7759  if(MidElement > -1)
7760  {
7762  }
7763  if(LagElement > -1)
7764  {
7766  }
7767  }
7768  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
7769 
7770  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
7771  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
7772 
7773  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
7774 
7775  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
7776  if(LeadElement > -1)
7777  // need to include this in case train exiting & no lead element
7778  {
7780  {
7781  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
7782  }
7783  }
7784  AValue = sqrt(2 * PowerAtRail / Mass);
7785 
7786  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
7787 
7788  // possible RestoreTimetableLocation + Marker, where Marker is
7789  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
7790  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
7791  // added at beta v0.2e
7792  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
7793  // name not allowed to include the '*' character
7794  {
7795  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
7796  bool GiveMessagesFalse = false;
7797  bool CheckLocationsExistInRailwayTrue = true;
7798  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
7799  {
7800  // otherwise take no action
7801  RestoreTimetableLocation = Location;
7802  }
7803  }
7804  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
7805 
7806  StoppedWithoutPower = false;
7807  if(Marker[6] == '1')
7808  {
7809  StoppedWithoutPower = true;
7810  }
7811  Utilities->CallLogPop(1458);
7812 }
7813 
7814 // ---------------------------------------------------------------------------
7815 
7816 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
7817 {
7819  {
7820  return(false); // HeadCode
7821 
7822  }
7823  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7824  {
7825  return(false); // RearStartElement
7826 
7827  }
7828  if(!Utilities->CheckFileInt(InFile, 0, 3))
7829  {
7830  return(false); // RearStartExitPos
7831 
7832  }
7833  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7834  {
7835  return(false); // StartSpeed
7836 
7837  }
7838  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7839  {
7840  return(false); // SignallerMaxSpeed
7841 
7842  }
7843  if(!Utilities->CheckFileBool(InFile))
7844  {
7845  return(false); // HoldAtLocationInTTMode
7846 
7847  }
7848  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7849  {
7850  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
7851 
7852  }
7853  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7854  {
7855  return(false); // IncrementalMinutes (max 96 x 60)
7856 
7857  }
7858  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7859  {
7860  return(false); // IncrementalDigits
7861 
7862  }
7863  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7864  {
7865  return(false); // Mass
7866 
7867  }
7868  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
7869  {
7870  return(false);
7871  }
7872  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
7873  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
7874  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7875  {
7876  return(false); // FrontElementLength
7877 
7878  }
7879  if(!Utilities->CheckFileDouble(InFile))
7880  {
7881  return(false); // EntrySpeed
7882 
7883  }
7884  if(!Utilities->CheckFileDouble(InFile))
7885  {
7886  return(false); // ExitSpeedHalf
7887 
7888  }
7889  if(!Utilities->CheckFileDouble(InFile))
7890  {
7891  return(false); // ExitSpeedFull
7892 
7893  }
7894  if(!Utilities->CheckFileDouble(InFile))
7895  {
7896  return(false); // TimetableMaxRunningSpeed
7897 
7898  }
7899  if(!Utilities->CheckFileDouble(InFile))
7900  {
7901  return(false); // MaxRunningSpeed
7902 
7903  }
7904  if(!Utilities->CheckFileDouble(InFile))
7905  {
7906  return(false); // MaxExitSpeed
7907 
7908  }
7909  if(!Utilities->CheckFileDouble(InFile))
7910  {
7911  return(false); // MaxBrakeRate
7912 
7913  }
7914  if(!Utilities->CheckFileDouble(InFile))
7915  {
7916  return(false); // BrakeRate
7917 
7918  }
7919  if(!Utilities->CheckFileDouble(InFile))
7920  {
7921  return(false); // PowerAtRail
7922 
7923  }
7924  if(!Utilities->CheckFileBool(InFile))
7925  {
7926  return(false); // FirstHalfMove
7927 
7928  }
7929  if(!Utilities->CheckFileBool(InFile))
7930  {
7931  return(false); // OneLengthAccelDecel
7932 
7933  }
7934  if(!Utilities->CheckFileDouble(InFile))
7935  {
7936  return(false); // double(EntryTime)
7937 
7938  }
7939  if(!Utilities->CheckFileDouble(InFile))
7940  {
7941  return(false); // double(ExitTimeHalf)
7942 
7943  }
7944  if(!Utilities->CheckFileDouble(InFile))
7945  {
7946  return(false); // double(ExitTimeFull)
7947 
7948  }
7949  if(!Utilities->CheckFileDouble(InFile))
7950  {
7951  return(false); // double(ReleaseTime)
7952 
7953  }
7954  if(!Utilities->CheckFileDouble(InFile))
7955  {
7956  return(false); // double(TRSTime)
7957 
7958  }
7959  if(!Utilities->CheckFileDouble(InFile))
7960  {
7961  return(false); // double(LastActionTime)
7962 
7963  }
7964  if(!Utilities->CheckFileBool(InFile))
7965  {
7966  return(false); // CallingOnFlag
7967 
7968  }
7969  if(!Utilities->CheckFileBool(InFile))
7970  {
7971  return(false); // BeingCalledOn
7972 
7973  }
7974  if(!Utilities->CheckFileBool(InFile))
7975  {
7976  return(false); // DepartureTimeSet
7977 
7978  }
7979  if(!Utilities->CheckFileInt(InFile, 0, 2))
7980  {
7981  return(false); // (short)TrainMode
7982 
7983  }
7984  if(!Utilities->CheckFileBool(InFile))
7985  {
7986  return(false); // TimetableFinished
7987 
7988  }
7989  if(!Utilities->CheckFileBool(InFile))
7990  {
7991  return(false); // LastActionDelayFlag
7992 
7993  }
7994  if(!Utilities->CheckFileBool(InFile))
7995  {
7996  return(false); // SignallerRemoved
7997 
7998  }
7999  if(!Utilities->CheckFileBool(InFile))
8000  {
8001  return(false); // TerminatedMessageSent
8002 
8003  }
8004  if(!Utilities->CheckFileBool(InFile))
8005  {
8006  return(false); // Derailed
8007 
8008  }
8009  if(!Utilities->CheckFileBool(InFile))
8010  {
8011  return(false); // DerailPending
8012 
8013  }
8014  if(!Utilities->CheckFileBool(InFile))
8015  {
8016  return(false); // Crashed
8017 
8018  }
8019  if(!Utilities->CheckFileBool(InFile))
8020  {
8021  return(false); // StoppedAtBuffers
8022 
8023  }
8024  if(!Utilities->CheckFileBool(InFile))
8025  {
8026  return(false); // StoppedAtSignal
8027 
8028  }
8029  if(!Utilities->CheckFileBool(InFile))
8030  {
8031  return(false); // StoppedAtLocation
8032 
8033  }
8034  if(!Utilities->CheckFileBool(InFile))
8035  {
8036  return(false); // SignallerStopped
8037 
8038  }
8039  if(!Utilities->CheckFileBool(InFile))
8040  {
8041  return(false); // StoppedAfterSPAD
8042 
8043  }
8044  if(!Utilities->CheckFileBool(InFile))
8045  {
8046  return(false); // StoppedForTrainInFront
8047 
8048  }
8049  if(!Utilities->CheckFileBool(InFile))
8050  {
8051  return(false); // NotInService
8052 
8053  }
8054  if(!Utilities->CheckFileBool(InFile))
8055  {
8056  return(false); // Plotted
8057 
8058  }
8059  if(!Utilities->CheckFileBool(InFile))
8060  {
8061  return(false); // TrainGone
8062 
8063  }
8064  if(!Utilities->CheckFileBool(InFile))
8065  {
8066  return(false); // SPADFlag
8067 
8068  }
8069  if(!Utilities->CheckFileBool(InFile))
8070  {
8071  return(false); // TimeTimeLocArrived
8072 
8073  }
8074  if(!Utilities->CheckFileInt(InFile, 0, 15))
8075  {
8076  return(false); // HOffset[0]
8077 
8078  }
8079  if(!Utilities->CheckFileInt(InFile, 0, 15))
8080  {
8081  return(false); // HOffset[1]
8082 
8083  }
8084  if(!Utilities->CheckFileInt(InFile, 0, 15))
8085  {
8086  return(false); // HOffset[2]
8087 
8088  }
8089  if(!Utilities->CheckFileInt(InFile, 0, 15))
8090  {
8091  return(false); // HOffset[3]
8092 
8093  }
8094  if(!Utilities->CheckFileInt(InFile, 0, 15))
8095  {
8096  return(false); // VOffset[0]
8097 
8098  }
8099  if(!Utilities->CheckFileInt(InFile, 0, 15))
8100  {
8101  return(false); // VOffset[1]
8102 
8103  }
8104  if(!Utilities->CheckFileInt(InFile, 0, 15))
8105  {
8106  return(false); // VOffset[2]
8107 
8108  }
8109  if(!Utilities->CheckFileInt(InFile, 0, 15))
8110  {
8111  return(false); // VOffset[3]
8112 
8113  }
8114  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8115  {
8116  return(false); // PlotElement[0]
8117 
8118  }
8119  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8120  {
8121  return(false); // PlotElement[1]
8122 
8123  }
8124  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8125  {
8126  return(false); // PlotElement[2]
8127 
8128  }
8129  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8130  {
8131  return(false); // PlotElement[3]
8132 
8133  }
8134  if(!Utilities->CheckFileInt(InFile, 0, 3))
8135  {
8136  return(false); // PlotEntryPos[0]
8137 
8138  }
8139  if(!Utilities->CheckFileInt(InFile, 0, 3))
8140  {
8141  return(false); // PlotEntryPos[1]
8142 
8143  }
8144  if(!Utilities->CheckFileInt(InFile, 0, 3))
8145  {
8146  return(false); // PlotEntryPos[2]
8147 
8148  }
8149  if(!Utilities->CheckFileInt(InFile, 0, 3))
8150  {
8151  return(false); // PlotEntryPos[3]
8152 
8153  }
8154  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8155  {
8156  return(false); // TrainCrashedInto
8157 
8158  }
8159  if(!Utilities->CheckFileInt(InFile, 0, 2))
8160  {
8161  return(false); // (short)Straddle
8162 
8163  }
8164  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8165  {
8166  return(false); // NextTrainID
8167 
8168  }
8169  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8170  {
8171  return(false); // TrainID
8172 
8173  }
8174  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8175  {
8176  return(false); // LeadElement
8177 
8178  }
8179  if(!Utilities->CheckFileInt(InFile, 0, 3))
8180  {
8181  return(false); // LeadEntryPos
8182 
8183  }
8184  if(!Utilities->CheckFileInt(InFile, 0, 3))
8185  {
8186  return(false); // LeadExitPos
8187 
8188  }
8189  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8190  {
8191  return(false); // MidElement
8192 
8193  }
8194  if(!Utilities->CheckFileInt(InFile, 0, 3))
8195  {
8196  return(false); // MidEntryPos
8197 
8198  }
8199  if(!Utilities->CheckFileInt(InFile, 0, 3))
8200  {
8201  return(false); // MidExitPos
8202 
8203  }
8204  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8205  {
8206  return(false); // LagElement
8207 
8208  }
8209  if(!Utilities->CheckFileInt(InFile, 0, 3))
8210  {
8211  return(false); // LagEntryPos
8212 
8213  }
8214  if(!Utilities->CheckFileInt(InFile, 0, 3))
8215  {
8216  return(false); // LagExitPos
8217 
8218  }
8219  if(!Utilities->CheckFileInt(InFile, 0, 14))
8220  {
8221  return(false);
8222  }
8223  // Background colour number //14 is failed colour at v2.4.0
8224  if(!Utilities->CheckFileBool(InFile))
8225  {
8226  return(false); // ForwardHeadCode
8227 
8228  }
8229  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8230  {
8231  return(false); // TrainDataEntryValue
8232 
8233  }
8234  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8235  {
8236  return(false); // ActionVectorEntryValue
8237 
8238  }
8240  {
8241  return(false); // End of train marker + possible RestoreTimetableLocation
8242 
8243  }
8244  // and StoppedWithoutPower flag
8245  return(true);
8246 }
8247 
8248 // ---------------------------------------------------------------------------
8249 
8250 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
8251 {
8252  // order below reflects significance so earlier shows first, as may have more than one flag set
8253  // only plot flashing trains when Flash is true
8254 
8255 /*
8256  clCrashedBackground (TColor)0x0000FF red
8257  clDerailedBackground (TColor)0x0000FF red
8258  clSPADBackground (TColor)0x00FFFF yellow
8259  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
8260  clCallOnBackground (TColor)0xFF33FF light magenta
8261  clSignalStopBackground (TColor)0x00FF66 green
8262  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
8263  clStationStopBackground (TColor)0xCCFFCC pale green
8264  clTRSBackground (TColor)0xFFCCFF light pink
8265  clBufferStopBackground (TColor)0xFFFFCC pale cyan
8266  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
8267  clSignallerStopped (TColor)0x99CCFF caramel
8268  clNormalBackground (TColor)0xCCCCCC grey
8269 */
8270 
8271  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
8272  bool HideFlashingTrain = true;
8273  // hide it when Flash false so it blinks on and off
8274  // if don't hide it it stays displayed all the time
8275  Graphics::TBitmap *SmallTrainBitmap;
8276 
8277  // NB ensure retain same order as zoomed in order so colours correspond
8279  {
8280  TrainController->CrashWarning = true;
8281  SmallTrainBitmap = RailGraphics->smRed;
8282  }
8284  {
8286  SmallTrainBitmap = RailGraphics->smRed;
8287  }
8289  {
8290  TrainController->SPADWarning = true;
8291  SmallTrainBitmap = RailGraphics->smYellow;
8292  }
8294  {
8296  SmallTrainBitmap = RailGraphics->smOrange;
8297  }
8299  {
8301  SmallTrainBitmap = RailGraphics->smMagenta;
8302  }
8304  {
8306  SmallTrainBitmap = RailGraphics->smBrightGreen;
8307  }
8309  {
8311  SmallTrainBitmap = RailGraphics->smCyan;
8312  }
8314  {
8315  SmallTrainBitmap = RailGraphics->smPaleGreen;
8316  HideFlashingTrain = false;
8317  }
8319  {
8320  SmallTrainBitmap = RailGraphics->smCyan;
8321  HideFlashingTrain = false;
8322  }
8324  {
8325  SmallTrainBitmap = RailGraphics->smLightBlue;
8326  HideFlashingTrain = false;
8327  }
8329  {
8330  SmallTrainBitmap = RailGraphics->smCaramel;
8331  HideFlashingTrain = false;
8332  }
8333  else
8334  {
8335  SmallTrainBitmap = RailGraphics->smBlack; // moving
8336  HideFlashingTrain = false;
8337  }
8338  // now plot the new train
8339  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
8340  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
8341  {
8342  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
8343  }
8344  if((MidElement > -1) && (!HideFlashingTrain || Flash))
8345  {
8346  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
8347  }
8348  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
8349  {
8350  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
8351  }
8355  Utilities->CallLogPop(1459);
8356 }
8357 
8358 // ---------------------------------------------------------------------------
8359 
8361 {
8362  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
8363  if(!Display->ZoomOutFlag)
8364  {
8365  Utilities->CallLogPop(1304);
8366  return;
8367  }
8368  for(int y = 0; y < 3; y++)
8369  {
8370  if(OldZoomOutElement[y] > -1)
8371  {
8372  bool FoundFlag = false;
8373  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
8374  TTrackElement IATElement1, IATElement2;
8375  // default elements to begin with
8376  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
8377  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
8378  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
8379  if(FoundFlag)
8380  {
8381  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
8382  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
8383  if(IMPair.first != IMPair.second)
8384  {
8385  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
8386  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
8387  }
8388  }
8389  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
8390  }
8391  }
8392  Utilities->CallLogPop(1305);
8393 }
8394 
8395 // ---------------------------------------------------------------------------
8396 
8397 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
8398 {
8399  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
8400  LocationName = "";
8401  if(!StoppedAtLocation)
8402  {
8403  Utilities->CallLogPop(1398);
8404  return(false);
8405  }
8406  if(LeadElement > -1)
8407  {
8409  }
8410  if((LocationName == "") && (MidElement > -1))
8411  {
8412  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
8413  }
8414  if((LocationName == "") && (LagElement > -1))
8415  {
8416  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
8417  }
8418  if(LocationName == "")
8419  {
8420  throw Exception("Error - Location name not set in TrainAtLocation");
8421  }
8422  Utilities->CallLogPop(1399);
8423  return(true);
8424 }
8425 
8426 // ---------------------------------------------------------------------------
8427 
8428 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
8429 {
8430  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
8431  for(int x = 0; x < 4; x++)
8432  {
8433  PlotTrainGraphic(7, x, Disp);
8434  }
8435  Utilities->CallLogPop(647);
8436 }
8437 
8438 // ---------------------------------------------------------------------------
8439 
8440 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
8441 {
8442  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
8443  for(int x = 0; x < 4; x++)
8444  {
8445  if(PlotElement[x] > -1)
8446  {
8447  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
8448  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
8449  }
8450  }
8451  Utilities->CallLogPop(1708);
8452 }
8453 
8454 // ---------------------------------------------------------------------------
8455 
8456 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
8457 {
8458  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
8459  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
8460  AnsiString(LinkNumber) + "," + HeadCode);
8461 
8462 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
8463  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
8464  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
8465 */
8466 
8467  // note that MidElement always fully occupied
8468  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
8469  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
8470  {
8471  Utilities->CallLogPop(2005);
8472  return(true);
8473  }
8474  if(Straddle == LeadMid)
8475  {
8476  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
8477  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
8478  {
8479  Utilities->CallLogPop(2006);
8480  return(true);
8481  }
8482  }
8483  else if(Straddle == LeadMidLag)
8484  {
8485  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
8486  // only interested in LeadEntryPos as train not occupying ExitPos yet
8487  {
8488  Utilities->CallLogPop(2007);
8489  return(true);
8490  }
8491  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
8492  // only interested in LagExitPos as train has left EntryPos
8493  {
8494  Utilities->CallLogPop(2008);
8495  return(true);
8496  }
8497  }
8498  Utilities->CallLogPop(2009);
8499  return(false);
8500 }
8501 
8502 // ---------------------------------------------------------------------------
8503 
8504 float TTrain::CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair) // only called for running trains.
8509 // TimeToExit & ExitPair added for multiplayer
8510 {
8511  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
8512  int DistanceToRedSignal = 0, DistanceToExit = -1;
8513  float TimeToAct = 0, LastTimeToExit = TimeToExit;
8514  TimeToExit = -1;
8515  ExitPair.first = -1;
8516  ExitPair.second = -1;
8517  float MinsEarly = 0; //added at v2.6.1
8518  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
8519  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
8520  float TempTTE;
8521 
8522  if(TrainFailed)
8523  {
8524  Utilities->CallLogPop(2147);
8525  return(0); // time to act now, time to exit set above
8526  }
8527  if(SignallerStopped)
8528  {
8529  Utilities->CallLogPop(2080);
8530  return(-1); //time to exit set above
8531  }
8532  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
8533  {
8534  Utilities->CallLogPop(2266);
8535  return(-1); // time to exit set above
8536  }
8537 
8538  // check if if exiting at a continuation, if so there's no action time needed but still need exit time & ExitPair
8539  if((LeadElement == -1) && (MidElement == -1) && (LagElement > -1) && (Track->TrackElementAt(1411, LagElement).TrackType == Continuation)
8540  && (ExitSpeedFull > 1)) //LagElement is the exit
8541  {
8542  if(Straddle == LeadMidLag) //only half of rear train element on exit, 0.5 lengths to exit
8543  {
8544  TempTTE = (0.5 * Track->TrackElementAt(1412, LagElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8545  if(TempTTE < LastTimeToExit)
8546  {
8547  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8548  }
8549  else
8550  {
8551  TimeToExit = LastTimeToExit;
8552  }
8553  ExitPair.first = Track->TrackElementAt(1413, LagElement).HLoc;
8554  ExitPair.second = Track->TrackElementAt(1414, LagElement).VLoc;
8555  Utilities->CallLogPop(2342);
8556  return(-1);
8557  }
8558  else
8559  {
8560  TimeToExit = 0; //all train exited
8561  ExitPair.first = Track->TrackElementAt(1415, LagElement).HLoc;
8562  ExitPair.second = Track->TrackElementAt(1416, LagElement).VLoc;
8563  Utilities->CallLogPop(2343);
8564  return(-1);
8565  }
8566  }
8567  if((LeadElement == -1) && (MidElement > -1) && (Track->TrackElementAt(1417, MidElement).TrackType == Continuation) && (ExitSpeedFull > 1))
8568  //here MidElement is the exit
8569  {
8570  if(Straddle == LeadMidLag) //front element of train half off the exit, 1.5 lengths to exit
8571  {
8572  TempTTE = (1.5 * Track->TrackElementAt(1418, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8573  if(TempTTE < LastTimeToExit)
8574  {
8575  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8576  }
8577  else
8578  {
8579  TimeToExit = LastTimeToExit;
8580  }
8581  ExitPair.first = Track->TrackElementAt(1419, MidElement).HLoc;
8582  ExitPair.second = Track->TrackElementAt(1420, MidElement).VLoc;
8583  Utilities->CallLogPop(2344);
8584  return(-1);
8585  }
8586  else //front element of train fully off the exit, one length to exit
8587  {
8588  TempTTE = (Track->TrackElementAt(1421, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8589  if(TempTTE < LastTimeToExit)
8590  {
8591  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8592  }
8593  else
8594  {
8595  TimeToExit = LastTimeToExit;
8596  }
8597  ExitPair.first = Track->TrackElementAt(1422, MidElement).HLoc;
8598  ExitPair.second = Track->TrackElementAt(1423, MidElement).VLoc;
8599  Utilities->CallLogPop(2345);
8600  return(-1);
8601  }
8602  }
8603  if(LeadElement > -1)
8604  {
8606  && (ExitSpeedFull > 1)) //LeadElement is the exit. If LeadMidLag have lead half on exit or if LeadMid have lead full on exit
8607  { //if LeadMidLag have 2.5 lengths to exit, else have 2 lengths to exit
8608  if(Straddle == LeadMidLag)
8609  {
8610  TempTTE = (2.5 * Track->TrackElementAt(1426, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8611  if(TempTTE < LastTimeToExit)
8612  {
8613  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8614  }
8615  else
8616  {
8617  TimeToExit = LastTimeToExit;
8618  }
8619  ExitPair.first = Track->TrackElementAt(1427, LeadElement).HLoc;
8620  ExitPair.second = Track->TrackElementAt(1428, LeadElement).VLoc;
8621  Utilities->CallLogPop(2346);
8622  return(-1);
8623  }
8624  else
8625  {
8626  TempTTE = (2 * Track->TrackElementAt(1429, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8627  if(TempTTE < LastTimeToExit)
8628  {
8629  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8630  }
8631  else
8632  {
8633  TimeToExit = LastTimeToExit;
8634  }
8635  ExitPair.first = Track->TrackElementAt(1430, LeadElement).HLoc;
8636  ExitPair.second = Track->TrackElementAt(1431, LeadElement).VLoc;
8637  Utilities->CallLogPop(2347);
8638  return(-1);
8639  }
8640  }
8641  }
8642 //here if LeadElement > -1 and its forward connection also > -1
8643 
8644  // calc distance to next red signal
8645  if(!Stopped() || StoppedAtLocation)
8646  {
8647  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
8648  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
8649  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
8650 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
8651  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
8652  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
8653  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
8654  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
8655  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
8656  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
8657  before the train has stopped the current station is still recognised as a future stop.
8658  In signaller mode stops don't count, and if pass red signal command is given then when have LeadMidLag the current element
8659  becomes the signal, and the time to act indication becomes 'NOW'.
8660 */
8661  {
8662  FirstPosToBeMeasured = LeadElement;
8663  FirstEntryPos = LeadEntryPos;
8664  }
8665  float CurrentStopTime; // set to 0 at start of function
8666  float LaterStopTime; // set to 0 at start of function
8667  float RecoverableTime; // set to 0 at start of function
8668  int AvTrackSpeed; // set to zero at start of function
8669  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
8670  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
8671  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
8672 //at this point can't have both DistanceToRedSignal and DistanceToExit both set. Either both will be unset (-1) or one will be set.
8673 //Therefore since need to calculate the time for each in the same way use a generic
8674 
8675  if((DistanceToRedSignal == -1) && (DistanceToExit == -1))// both unset so no action needed
8676  {
8677  TimeToExit = -1;
8678  Utilities->CallLogPop(2076);
8679  return(-1);
8680  }
8681 //else one or other is set
8682  bool DistanceToRedSignalSet = (DistanceToRedSignal > -1);
8683  bool DistanceToExitSet = (DistanceToExit > -1);
8684  int GenericDistance = DistanceToRedSignal;
8685  if(DistanceToExitSet)
8686  {
8687  GenericDistance = DistanceToExit;
8688  }
8689 /* Have MinsDelayed; pos or neg,
8690  CurrentStopTime; pos or zero
8691  LaterStopTime; pos or zero
8692  RecoverableTime; pos or zero
8693 
8694  & from these calculate TotalStopTime. noting that:
8695  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
8696  RecoverableTime always < LaterStopTime or both zero
8697  can't subtract more than RecoverableTime (MinsDelayed > 0)
8698  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
8699  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
8700  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
8701  if running early & stopped at location CurrentStopTime will automatically include the excess
8702 */
8703  float TimeToSubtract, TotalStopTime;
8704  if(MinsDelayed > RecoverableTime)
8705  {
8706  TimeToSubtract = RecoverableTime;
8707  }
8708  else
8709  {
8710  TimeToSubtract = MinsDelayed; // may be negative;
8711  }
8712  if((AvTrackSpeed > 0) && (DistanceToStationStop <= GenericDistance) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
8713  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
8714  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
8715  //next station stop
8716  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
8717  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
8718  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
8719  //first find departure time from the next stop
8720  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
8721  {
8722  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
8723  {
8726  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8727  }
8728  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
8729  {
8731  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8732  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
8733  {
8734  // must be a departure
8735  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
8736  }
8737  }
8738  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
8739  {
8740  MinsEarly = 0;
8741  }
8742  if(MinsEarly < 0)
8743  {
8744  MinsEarly = 0;
8745  }
8746  }
8747  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
8748  {
8749  if(CurrentStopTime > 0)
8750  {
8751  TotalStopTime = CurrentStopTime + LaterStopTime;
8752  }
8753  // stopped at loc, will depart on time
8754  else
8755  {
8756  TotalStopTime = LaterStopTime - MinsDelayed;
8757  }
8758  // not stopped, will depart on time at first later stop so add the delay
8759  }
8760  else if((MinsEarly > 0) && !Stopped()) //running early
8761  {
8762  TotalStopTime = LaterStopTime + MinsEarly;
8763  }
8764  else // on time or running late
8765  {
8766  if(LaterStopTime == 0)
8767  {
8768  TotalStopTime = CurrentStopTime;
8769  }
8770  // no later stops, if stopped now will depart as soon as possible,
8771  // if not stopped no stop times to add
8772  else
8773  {
8774  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
8775  }
8776  }
8777  if(AvTrackSpeed < 30)
8778  {
8779  AvTrackSpeed = 30;
8780  }
8781  int Speed = AvTrackSpeed;
8782  if(AvTrackSpeed > int(MaxRunningSpeed))
8783  {
8784  Speed = int(MaxRunningSpeed);
8785  }
8786  if(TrainMode == Signaller)
8787  {
8788  Speed = SignallerMaxSpeed;
8789  TotalStopTime = 0;
8790  }
8791  if(DistanceToRedSignalSet)
8792  {
8793  TimeToAct = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
8794  // accel & decel taken into account in
8795  // CalcDistanceToRedSignalandStopTime
8796  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
8797  TimeToExit = -1;
8798  Utilities->CallLogPop(2079);
8799  return(TimeToAct);
8800  }
8801  else //DistanceToExitSet must be true
8802  {
8803  TimeToExit = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
8804  // accel & decel taken into account in
8805  // CalcDistanceToRedSignalandStopTime
8806  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
8807  Utilities->CallLogPop(2370);
8808  return(-1); //no red signal so no time to act
8809  }
8810  }
8811  else // stopped not at location
8812  {
8814  {
8815  TimeToAct = 0.0;
8816  TimeToExit = -1;
8817  }
8818  // but if stopped at a signal & autosigs route after it then ok
8819  if(StoppedAtSignal)
8820  {
8821  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
8822  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
8823  int NextExitPos;
8824  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
8825  {
8826  if((NextEntryPos == 0) || (NextEntryPos == 2))
8827  // leading entry point
8828  {
8829  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
8830  {
8831  NextExitPos = 1;
8832  }
8833  else
8834  {
8835  NextExitPos = 3;
8836  }
8837  }
8838  else
8839  {
8840  NextExitPos = 0; // trailing entry point
8841  }
8842  }
8843  else
8844  {
8845  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
8846  }
8847  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
8848  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
8849  int RouteNumber; // holder for referenced value, not used
8850  if(AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8851  {
8852  TimeToAct = -1;
8853  TimeToExit = -1;
8854  }
8855  }
8856  Utilities->CallLogPop(2074);
8857  return(TimeToAct);
8858  }
8859 }
8860 
8861 // ---------------------------------------------------------------------------
8862 
8864 {
8865  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
8866  if(LeadElement > -1)
8867  {
8869  {
8870  Utilities->CallLogPop(2148);
8871  return(true);
8872  }
8873  }
8874  if(MidElement > -1)
8875  {
8877  {
8878  Utilities->CallLogPop(2149);
8879  return(true);
8880  }
8881  }
8882  if(LagElement > -1)
8883  {
8885  {
8886  Utilities->CallLogPop(2150);
8887  return(true);
8888  }
8889  }
8890  Utilities->CallLogPop(2151);
8891  return(false);
8892 }
8893 
8894 // ---------------------------------------------------------------------------
8895 // TTrainController
8896 // ---------------------------------------------------------------------------
8897 
8899 {
8900  OnTimeArrivals = 0;
8901  LateArrivals = 0;
8902  EarlyArrivals = 0;
8903  OnTimePasses = 0;
8904  LatePasses = 0;
8905  EarlyPasses = 0;
8906  OnTimeExits = 0; //these 3 exits added at v2.9.2 - missed in error earlier
8907  LateExits = 0;
8908  EarlyExits = 0;
8909  OnTimeDeps = 0;
8910  LateDeps = 0;
8911  MissedStops = 0;
8912  OtherMissedEvents = 0;
8913  UnexpectedExits = 0;
8914  NumFailures = 0;
8915  IncorrectExits = 0;
8916  SPADEvents = 0;
8917  SPADRisks = 0;
8918  CrashedTrains = 0;
8919  Derailments = 0;
8920  TotArrDepPass = 0;
8921  TotLateArrMins = 0;
8922  TotEarlyArrMins = 0;
8923  TotLatePassMins = 0;
8924  TotEarlyPassMins = 0;
8925  TotLateExitMins = 0; //added at v2.9.1
8926  TotEarlyExitMins = 0; //added at v2.9.1
8927  TotLateDepMins = 0;
8928  ExcessLCDownMins = 0;
8929  SkippedTTEvents = 0; //added at v2.11.0
8930  TTClockTime = 0; // added for v0.6
8932  // added at v1.3.0 to ensure false at start
8933  OpTimeToActUpdateCounter = 0; // new v2.2.0
8934  OpActionPanelVisible = false; // new v2.2.0
8935  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
8936  SSHigh = false;
8937  MRSHigh = false;
8938  MRSLow = false;
8939  MassHigh = false;
8940  BFHigh = false;
8941  BFLow = false;
8942  PwrHigh = false;
8943  SigSHigh = false;
8944  SigSLow = false;
8945  randomize();
8946  // to seed rand() & random() with a random number (see UpdateTrain)
8947 }
8948 
8949 // ---------------------------------------------------------------------------
8950 
8952 {
8953  for(unsigned int x = 0; x < TrainVector.size(); x++)
8954  {
8955  TrainVectorAt(32, x).DeleteTrain(4);
8956  }
8957  TrainVector.clear();
8958 }
8959 
8960 // ---------------------------------------------------------------------------
8961 
8962 void TTrainController::LogEvent(AnsiString Str)
8963 {
8964  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
8965 
8966  // restrict to last 1000 entries
8967  Utilities->EventLog.push_back(FullStr);
8968  if(Utilities->EventLog.size() > 1000)
8969  {
8970  Utilities->EventLog.pop_front();
8971  }
8972 }
8973 
8974 // ---------------------------------------------------------------------------
8975 
8977 {
8978  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
8979  bool ClockState = Utilities->Clock2Stopped;
8980  Utilities->Clock2Stopped = true;
8981  // new section dealing with Snt & Snt-sh additions
8982  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
8983  // clock tick after stops flashing
8985  {
8986  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
8987  {
8988  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
8989  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
8990  TActionEventType EventType = NoEvent;
8991  if(AVEntry0.Command == "Snt")
8992  {
8993  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8994  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8995  int IncrementalMinutes = 0;
8996  int IncrementalDigits = 0;
8997  if(AVEntryLast.FormatType == Repeat)
8998  {
8999  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9000  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9001  }
9002  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9003  {
9004  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
9005  }
9006  // see above note
9007 
9008  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9009  {
9010  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
9011  if(TTOD.RunningEntry != NotStarted)
9012  {
9013  continue;
9014  }
9015 
9016 //Multiplayer: here check for a train entering at a coupling {RearStartOrRepeatMins shows if it's a coupling or not), and can only be a Snt entry
9017 //if so and no arrival signalled yet bypass the timetabled arrival
9018 //if so and arrival signalled then start the new service, using the repeat number and headcode for the entering train
9019 //if a repeat is skipped then should be ok if it arrives later as its RunningEntry is still NotStarted
9020 
9021  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
9022  {
9023  break; // all the rest will also be greater
9024  }
9025  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
9026  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9027  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
9028  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
9029  {
9030  TTOD.TrainID = TrainVector.back().TrainID;
9031  TTOD.RunningEntry = Running;
9032  }
9033  else if(EventType == FailTrainEntry)
9034  {
9035  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9036  }
9037  }
9038  }
9039  if(AVEntry0.Command == "Snt-sh")
9040  // just start this once, shuttle repeats take care of restarts
9041  {
9042  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9043  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9044  int IncrementalMinutes = 0;
9045  int IncrementalDigits = 0;
9046  if(AVEntryLast.FormatType == Repeat)
9047  {
9048  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9049  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9050  }
9051  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9052  {
9053  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
9054  }
9055  // see above note
9056  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
9057  if(TTOD.RunningEntry == NotStarted)
9058  {
9059  if(AVEntry0.EventTime <= TTClockTime)
9060  {
9061  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9062  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
9063  TDEntry.SignallerSpeed, false, EventType))
9064  // false for SignallerControl
9065  {
9066  TTOD.TrainID = TrainVector.back().TrainID;
9067  TTOD.RunningEntry = Running;
9068  }
9069  else if(EventType == FailTrainEntry)
9070  {
9071  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9072  }
9073  }
9074  }
9075  }
9076  }
9077  }
9078 
9079  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
9080  // iteration, next cycle will catch up with any other pending updates
9081  if(!TrainVector.empty())
9082  {
9083  TrainAdded = false;
9084  AllRoutes->CallonVector.clear(); // this will be rebuilt during the calls to UpdateTrain
9085  for(unsigned int x = 0; x < TrainVector.size(); x++)
9086  {
9087  TrainVectorAt(33, x).UpdateTrain(0);
9088  /* added HasTrainGone() condition below in v0.4c to prevent 2 trains both having TrainGone set in UpdateTrain
9089  at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
9090  iterates in reverse to erase the second train to have gone (when the first train to have gone comes before the second in TrainVector),
9091  but afterwards ReplotTrains iterates forwards and therefore replots the first train to have gone and therefore sets the TrainIDOnElement value
9092  to the exited train, with nothing to reset it. Hovering the mouse over that element with train information enabled causes an error because
9093  the track element thinks the train is still there, whereas it is missing from the TrainVector. BUT subsequently (in v2.11.1) changed RePlotTrains
9094  so it doesn't plot trains with TrainGone set, but left this is as does no harm
9095 
9096  Had another error notified by Kevin Smith on 02/01/22 where a train was manually removed in the same clock cycle as a train exited, and this caused
9097  the same error as above. Did a lot of experimenting but eventually cured it with two changes, first as above in RePlotTrains, and also below adding
9098  a break; command after one TrainHasGone() dealt with. There were introduced in v2.11.1 & seems ok now
9099 
9100  These changes should deal with any number of TrainGone flags set in the same clock cycle - from exiting, manual removal, or joins
9101  */
9102  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
9103  {
9104  break; //only one exited train will be dealt with at a time (see below) so no point looking further
9105  }
9106  }
9107  // set warning flags
9108  CrashWarning = false;
9109  DerailWarning = false;
9110  SPADWarning = false;
9111  CallOnWarning = false;
9112  SignalStopWarning = false;
9113  BufferAttentionWarning = false;
9114  TrainFailedWarning = false;
9115  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
9116  {
9117  TTrain &Train = TrainVectorAt(34, x);
9118  if(Train.Crashed)
9119  // can't use background colours for crashed & derailed because same colour
9120  {
9121  CrashWarning = true;
9122  }
9123  else if(Train.Derailed)
9124  // can't use background colours for crashed & derailed because same colour
9125  {
9126  DerailWarning = true;
9127  }
9128  else if(Train.BackgroundColour == clSPADBackground)
9129  // use colour as that changes as soon as passes signal
9130  {
9131  SPADWarning = true;
9132  }
9133  else if(Train.BackgroundColour == clTrainFailedBackground)
9134  {
9135  TrainFailedWarning = true;
9136  }
9137  else if(Train.BackgroundColour == clCallOnBackground)
9138  // use colour as also stopped at signal
9139  {
9140  CallOnWarning = true;
9141  }
9142  else if(Train.BackgroundColour == clSignalStopBackground)
9143  // use colour to distinguish from call-on
9144  {
9145  SignalStopWarning = true;
9146  }
9147  else if(Train.BackgroundColour == clBufferAttentionNeeded)
9148  // use colour to distinguish from ordinary buffer stop
9149  {
9150  BufferAttentionWarning = true;
9151  }
9152  if(Train.HasTrainGone())
9153  {
9154  AnsiString Loc = "";
9155  bool ElementFound = false;
9156  TTrackElement TE;
9157  if(Train.LagElement > -1)
9158  {
9159  TE = Track->TrackElementAt(531, Train.LagElement);
9160  ElementFound = true;
9161  }
9162  else if(Train.MidElement > -1)
9163  {
9164  TE = Track->TrackElementAt(779, Train.MidElement);
9165  ElementFound = true;
9166  }
9167  else if(Train.LeadElement > -1)
9168  {
9169  TE = Track->TrackElementAt(780, Train.LeadElement);
9170  ElementFound = true;
9171  }
9172  if(ElementFound)
9173  {
9174  if(TE.ActiveTrackElementName != "")
9175  {
9176  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9177  }
9178  else
9179  {
9180  Loc = "track element " + TE.ElementID;
9181  }
9182  }
9183  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
9184  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
9185  // need above first because may also have ActionVectorEntryPtr == "Fer"
9186  {
9187  Train.UnplotTrain(9);
9188  // added at v1.3.0 to reset signals after train removed from an autosigsroute
9190  {
9193  }
9194  // end of addition
9195  AllRoutes->RebuildRailwayFlag = true;
9196  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
9197  // correctly after a crash
9198  }
9199  else if(AVEntryPtr->Command == "Fer")
9200  {
9201  bool CorrectExit = false;
9202  if(!AVEntryPtr->ExitList.empty())
9203  {
9204  for(TNumListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
9205  {
9206  if(*ELIT == Train.LagElement)
9207  {
9208  CorrectExit = true;
9209  }
9210  }
9211  }
9212  if(CorrectExit)
9213  {
9214  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
9215  }
9216  else
9217  {
9218  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
9219  }
9220  }
9221  else
9222  {
9223  if(!AVEntryPtr->SignallerControl)
9224  {
9225  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
9226  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
9227  // -2 is marker for send messages for all remaining actions except Fer if present
9228  }
9229  else
9230  {
9231  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
9232  }
9233  }
9234  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
9235  Train.DeleteTrain(1);
9236  TrainVector.erase(TrainVector.begin() + x);
9237  ReplotTrains(1, Display); //to reset ElementIDs for remaining trains when have removed a train
9238  //NB: won't plot any trains with TrainGone flag set (changed at v2.11.1)
9239  break; //added at v2.11.1 to ensure that only one train with TrainGone set is dealt with in one clock cycle
9240  }
9241  }
9242  }
9243  else
9244  {
9245  // reset all flags in case last train removed with flag set
9246  CrashWarning = false;
9247  DerailWarning = false;
9248  SPADWarning = false;
9249  CallOnWarning = false;
9250  SignalStopWarning = false;
9251  BufferAttentionWarning = false;
9252  TrainFailedWarning = false;
9253  }
9254  // update OpTimeToActMultimap
9256  {
9258  // clears entries then adds values for running trains then for continuation entries
9260  //added for multiplayer for running trains only
9261  }
9262  Utilities->Clock2Stopped = ClockState;
9263  Utilities->CallLogPop(723);
9264 }
9265 
9266 // ---------------------------------------------------------------------------
9268 {
9269  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
9270  if(!TrainVector.empty())
9271  {
9272  for(int x = TrainVector.size() - 1; x >= 0; x--)
9273  {
9274  TrainVectorAt(50, x).DeleteTrain(2);
9275  }
9276  TrainVector.clear();
9277  }
9278  if(!TrainDataVector.empty())
9279  {
9280  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9281  {
9282  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
9283  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9284  {
9285  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
9286  TOD.RunningEntry = NotStarted;
9287  TOD.TrainID = -1;
9288  TOD.EventReported = NoEvent;
9289  }
9290  }
9291  }
9292  Display->GetOutputLog1()->Caption = "";
9293  Display->GetOutputLog2()->Caption = "";
9294  Display->GetOutputLog3()->Caption = "";
9295  Display->GetOutputLog4()->Caption = "";
9296  Display->GetOutputLog5()->Caption = "";
9297  Display->GetOutputLog6()->Caption = "";
9298  Display->GetOutputLog7()->Caption = "";
9299  Display->GetOutputLog8()->Caption = "";
9300  Display->GetOutputLog9()->Caption = "";
9301  Display->GetOutputLog10()->Caption = "";
9302  Utilities->CallLogPop(1352);
9303 }
9304 
9305 // ---------------------------------------------------------------------------
9306 
9308 {
9309  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
9310  if(!TrainVector.empty())
9311  {
9312  for(unsigned int x = 0; x < TrainVector.size(); x++)
9313  {
9314  if(!TrainVectorAt(84, x).HasTrainGone()) //added at v2.11.0 to prevent plotting a train pending removal & particularly to prevent TrainElementID's being reinstated
9315  { //see Kevin Smith error information for details
9316  TrainVectorAt(51, x).PlotTrain(4, Disp);
9317  }
9318  }
9319  }
9320  Utilities->CallLogPop(724);
9321 }
9322 
9323 // ---------------------------------------------------------------------------
9324 
9325 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
9326 {
9327  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
9328  if(!TrainVector.empty())
9329  {
9330  for(unsigned int x = 0; x < TrainVector.size(); x++)
9331  {
9332  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
9333  }
9334  }
9335  Utilities->CallLogPop(1707);
9336 }
9337 
9338 // ---------------------------------------------------------------------------
9339 
9341 {
9342  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
9343  if(!TrainVector.empty())
9344  {
9345  for(unsigned int x = 0; x < TrainVector.size(); x++)
9346  {
9347  TrainVectorAt(52, x).UnplotTrain(10);
9348  }
9349  }
9351  Utilities->CallLogPop(725);
9352 }
9353 
9354 // ---------------------------------------------------------------------------
9355 
9356 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
9357  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
9358  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
9359 {
9360  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
9361  "," + AnsiString(Mass) + "," + ModeStr);
9362  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
9363  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr); //at v2.11.1 dropped later headcode - was listed twice
9364 
9365  int RearExitPos = -1;
9366 
9367  for(int x = 0; x < 4; x++)
9368  {
9369  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
9370  {
9371  RearExitPos = x;
9372  }
9373  }
9374  if(RearExitPos == -1)
9375  {
9376  throw Exception("Error, RearExit == -1 in AddTrain");
9377  }
9378  bool ReportFlag = true;
9379 
9380  // used to stop repeated messages from CheckStartAllowable when split failed
9381  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
9382  {
9383  ReportFlag = false;
9384  }
9385  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
9386  {
9387  // messages sent to performance log in CheckStartAllowable if ReportFlag true
9388  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
9389  Utilities->CallLogPop(938);
9390  return(false);
9391  }
9392  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
9393  TTrainMode TrainMode = NoMode;
9394 
9395  if(ModeStr == "Timetable")
9396  {
9397  TrainMode = Timetable;
9398  }
9399  // all else gives 'None', 'Signaller' set within program
9400 
9401  if(MaxRunningSpeed < 10)
9402  {
9403  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
9404  }
9405  if(SignallerSpeed < 10)
9406  {
9407  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
9408  }
9409  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
9410  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
9411 
9412  LogEvent("AddTrainSupplemental: Service Ref = " + TrainDataEntryPtr->ServiceReference + ", TrainID = " + AnsiString(NewTrain->TrainID)); //new at v2.11.1 so can relate headcode to ID
9413 
9414  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
9415  // initialise here rather than in TTrain constructor as create trains
9416  // with Null TrainDataEntryPtr when loading session trains
9417  if(SignallerControl)
9418  {
9419  NewTrain->TimetableFinished = true;
9420  NewTrain->SignallerStoppingFlag = false;
9421  NewTrain->TrainMode = Signaller;
9422  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
9423  {
9424  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
9425  }
9427  }
9428  // deal with starting conditions:-
9429  // unlocated Snt: just report entry & advance pointer
9430  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
9431  // Sns doesn't need a new train
9432  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
9433  // covers all above located starts
9434  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
9435  // wouldn't have accepted the timetable
9436  {
9437  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
9438  // StoppedAtBuffers is set in UpdateTrain()
9439  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
9440  // buffer end must be ahead of train or would have failed start position check
9441  {
9442  NewTrain->StoppedAtLocation = true;
9443  NewTrain->PlotStartPosition(0);
9445  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9446  NewTrain->ActionVectorEntryPtr->Warning);
9447  if(!SignallerControl) // don't advance if SignalControlEntry
9448  {
9449  NewTrain->ActionVectorEntryPtr++;
9450  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
9451  }
9452  NewTrain->LastActionTime = TTClockTime;
9453  }
9454  // else a through station stop
9455  else
9456  {
9457  NewTrain->StoppedAtLocation = true;
9458  NewTrain->PlotStartPosition(10);
9460  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9461  NewTrain->ActionVectorEntryPtr->Warning);
9462  if(!SignallerControl) // don't advance if SignalControlEntry
9463  {
9464  NewTrain->ActionVectorEntryPtr++;
9465  }
9466  NewTrain->LastActionTime = TTClockTime;
9467  }
9468  }
9469  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
9470  {
9471  NewTrain->PlotStartPosition(11);
9472  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
9473  AnsiString Loc = "";
9474  if(TE.ActiveTrackElementName != "")
9475  {
9476  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9477  }
9478  else
9479  {
9480  Loc = "track element " + TE.ElementID;
9481  }
9482  if(TE.TrackType == Continuation)
9483  {
9484  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9485  }
9486  else
9487  {
9488  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9489  }
9490  if(!SignallerControl) // don't advance if SignalControlEntry
9491  {
9492  NewTrain->ActionVectorEntryPtr++;
9493  }
9494  NewTrain->LastActionTime = TTClockTime;
9495  // no need to set LastActionTime for an unlocated entry
9496  }
9497  // cancel a wrong-direction route if either element of train starts on one
9498  if(NewTrain->LeadElement > -1)
9499  {
9500  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
9501  }
9502  if(NewTrain->MidElement > -1)
9503  {
9504  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
9505  }
9506  // set signals for a right-direction autosigs route for either element of train on one
9507  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
9508  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
9509  int RouteNumber = -1;
9510  bool SignalsSet = false;
9511 
9512  if(NewTrain->LeadElement > -1)
9513  {
9514  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9515  {
9516  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9517  int RouteStartPosition;
9518  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9519  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
9520  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
9521  if(FirstPair.first == RouteNumber)
9522  {
9523  RouteStartPosition = FirstPair.second;
9524  }
9525  else if(SecondPair.first == RouteNumber)
9526  {
9527  RouteStartPosition = SecondPair.second;
9528  }
9529  else
9530  {
9531  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
9532  }
9533  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
9534  SignalsSet = true;
9535  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9536  }
9537  else if(RouteNumber > -1) // non-autosigsroute
9538  {
9539  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
9540  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9541  int FirstELinkPos = TempPDE.GetELinkPos();
9542  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
9543  {
9544  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9545  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
9546  }
9547  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
9548  {
9549  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9550  // remove the last element under LeadElement
9551  }
9552  AllRoutes->RebuildRailwayFlag = true;
9553  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9554  // now deal with a rear linked autosigs route
9555  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
9556  {
9557  int LinkedRouteNumber = -1;
9558  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
9559  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9560  {
9561  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
9562  // this is ok as here we are setting signals from the start of the route
9563  }
9564  }
9565  SignalsSet = true;
9566  }
9567  }
9568  if(NewTrain->MidElement > -1)
9569  // if entering at a continuation MidElement == -1
9570  {
9571  // this is included in case a train starts with LeadElement on no route and MidElement on a route
9572  if(!SignalsSet)
9573  {
9574  RouteNumber = -1;
9575  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9576  {
9577  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9578  int RouteStartPosition;
9579  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9580  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
9581  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
9582  if(FirstPair.first == RouteNumber)
9583  {
9584  RouteStartPosition = FirstPair.second;
9585  }
9586  else if(SecondPair.first == RouteNumber)
9587  {
9588  RouteStartPosition = SecondPair.second;
9589  }
9590  else
9591  {
9592  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
9593  }
9594  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
9595  SignalsSet = true;
9596  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9597  }
9598  else if(RouteNumber > -1) // non-autosigsroute
9599  {
9600  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
9601  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9602  int FirstELinkPos = TempPDE.GetELinkPos();
9603  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
9604  {
9605  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9606  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
9607  }
9608  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
9609  {
9610  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9611  // remove the last element under LeadElement
9612  }
9613  AllRoutes->RebuildRailwayFlag = true;
9614  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9615  // now deal with a rear linked autosigs route
9616  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
9617  {
9618  int LinkedRouteNumber = -1;
9619  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
9620  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9621  {
9622  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
9623  // this is ok as now we are setting signals from the start of the route
9624  }
9625  }
9626  }
9627  }
9628  }
9629  TrainVector.push_back(*NewTrain);
9630  Utilities->CallLogPop(731);
9631  return(true);
9632 }
9633 
9634 // ---------------------------------------------------------------------------
9635 
9636 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
9637 {
9638  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
9639  AnsiString(TrackVectorNumber));
9640  int VecPos = -1;
9641 
9642  for(unsigned int x = 0; x < TrainVector.size(); x++)
9643  {
9644  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
9645  {
9646  VecPos = x;
9647  }
9648  }
9649  if(VecPos == -1)
9650  {
9651  throw Exception("Error, VecPos not set in EntryPos");
9652  }
9653  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
9654  {
9655  Utilities->CallLogPop(734);
9656  return(TrainVectorAt(3, VecPos).LeadEntryPos);
9657  }
9658  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
9659  {
9660  Utilities->CallLogPop(735);
9661  return(TrainVectorAt(5, VecPos).MidEntryPos);
9662  }
9663  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
9664  {
9665  Utilities->CallLogPop(736);
9666  return(TrainVectorAt(7, VecPos).LagEntryPos);
9667  }
9668  Utilities->CallLogPop(737);
9669  return(-1);
9670 }
9671 
9672 // ---------------------------------------------------------------------------
9673 
9675 {
9676  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
9677  for(unsigned int x = 0; x < TrainVector.size(); x++)
9678  {
9679  if(TrainVectorAt(53, x).TrainID == TrainID)
9680  {
9681  Utilities->CallLogPop(738);
9682  return(TrainVectorAt(54, x));
9683  }
9684  }
9685  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
9686 }
9687 
9688 // ---------------------------------------------------------------------------
9689 
9690 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
9691 // return true if find the train (added at v2.4.0 as can select a removed train
9692 // in OAListBox before it updates - reported by LiWinDom in error report 23/04/20)
9693 {
9694  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
9695  for(unsigned int x = 0; x < TrainVector.size(); x++)
9696  {
9697  if(TrainVectorAt(69, x).TrainID == TrainID)
9698  {
9699  Utilities->CallLogPop(2152);
9700  return(true);
9701  }
9702  }
9703  Utilities->CallLogPop(2153);
9704  return(false);
9705 }
9706 
9707 // ---------------------------------------------------------------------------
9708 
9709 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
9710 {
9711  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
9712  Utilities->Format96HHMMSS(Time));
9713  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
9714 
9715  Utilities->CallLogPop(2061);
9716  return(RepeatTime);
9717 }
9718 
9719 // ---------------------------------------------------------------------------
9720 
9721 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
9722 // Enter with Ptr pointing to first action to be listed (i.e. next action)
9723 {
9724  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
9725  AnsiString RetStr = "", PartStr = "";
9726  int Count = 0;
9727  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
9728 
9729  Ptr--; // because incremented at start of loop
9730  do
9731  {
9732  Ptr++;
9733  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
9734  {
9735  continue; // move past the starting entry
9736  }
9737  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
9738  {
9739  break;
9740  }
9741  if(Ptr->SignallerControl)
9742  {
9743  RetStr = "Train under signaller control";
9744  break;
9745  }
9746  if(Ptr->FormatType == TimeTimeLoc)
9747  {
9748  if(Ptr->ArrivalTime == Ptr->DepartureTime)
9749  {
9750  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
9751  }
9752  else
9753  {
9754  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
9755  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9756  Count++; // because there are 2 entries
9757  }
9758  }
9759  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
9760  {
9761  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
9762  }
9763  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
9764  {
9765  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9766  }
9767  else if(Ptr->FormatType == PassTime) // new
9768  {
9769  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
9770  }
9771  else if(Ptr->Command == "Fns")
9772  {
9773  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9774  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9775  PartStr = ControllerCheckNewServiceDepartureTime(0, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
9776  }
9777  else if(Ptr->Command == "F-nshs")
9778  {
9779  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9780  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
9781  PartStr = ControllerCheckNewServiceDepartureTime(1, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9782  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
9783  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
9784  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
9785  }
9786 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
9787  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9788  {
9789  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9790  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9791  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9792  PartStr = ControllerCheckNewServiceDepartureTime(2, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9793  }
9794  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9795  {
9796  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9797  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
9798  PartStr = ControllerCheckNewServiceDepartureTime(3, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9799  }
9800  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9801  {
9802  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9803  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9804  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9805  PartStr = ControllerCheckNewServiceDepartureTime(4, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9806  }
9807  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9808  {
9809  PartStr = "Terminate at " + Ptr->LocationName;
9810  }
9811  else if(Ptr->Command == "Frh")
9812  {
9813  PartStr = "Terminate at " + Ptr->LocationName;
9814  }
9815  else if(Ptr->Command == "Fer")
9816  {
9817  AnsiString AllowedExits;
9818  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
9819  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
9820  }
9821  else if(Ptr->Command == "Fjo")
9822  {
9823  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
9824  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9825  }
9826  else if(Ptr->Command == "jbo")
9827  {
9828  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
9829  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9830  }
9831  else if(Ptr->Command == "fsp")
9832  {
9833  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
9834  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9835  }
9836  else if(Ptr->Command == "rsp")
9837  {
9838  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
9839  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9840  }
9841  else if(Ptr->Command == "cdt")
9842  {
9843  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
9844  }
9845  if(RetStr != "")
9846  {
9847  RetStr = RetStr + '\n' + PartStr;
9848  }
9849  else
9850  {
9851  RetStr = PartStr;
9852  }
9853  Count++;
9854  }
9855  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
9856  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
9857  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
9858  // forward as anyone should wish to see without looking at the full timetable
9859  Utilities->CallLogPop(2072);
9860  return(RetStr);
9861 }
9862 
9863 // ---------------------------------------------------------------------------
9864 
9865 AnsiString TTrainController::ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
9866 {
9867  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
9868  + AnsiString(RptNum) + ",ControllerCheckNewServiceDepartureTime," + TDEPtr->HeadCode);
9869  AnsiString DepTime = "", EventTime = "";
9870  bool CDTFlag = false; //reports if train changes direction before departs
9871  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
9872  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
9873  {
9874  if(AVI->Command == "cdt")
9875  {
9876  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
9877  continue;
9878  }
9879  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
9880  {
9881  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
9882  RetStr += "\nNew service splits at " + EventTime;
9883  Utilities->CallLogPop(2237);
9884  return(RetStr);
9885  }
9886  if(AVI->Command == "jbo")
9887  {
9888  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
9889  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
9890  Utilities->CallLogPop(2238);
9891  return(RetStr);
9892  }
9893  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
9894  {
9895  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
9896  if(CDTFlag)
9897  {
9898  RetStr += "\nNew service changes direction then departs at " + DepTime;
9899  }
9900  else
9901  {
9902  RetStr += "\nNew service departs at " + DepTime;
9903  }
9904  Utilities->CallLogPop(2239);
9905  return(RetStr);
9906  }
9907  }
9908  Utilities->CallLogPop(2223);
9909  return(RetStr);
9910 }
9911 
9912 // ---------------------------------------------------------------------------
9913 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
9914 /*
9915  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
9916  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
9917  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
9918 
9919  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
9920  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
9921  user wishes
9922 
9923  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9924  descriptive text or anything user wishes
9925  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9926  be ignored) is taken as the timetable start time.
9927  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9928  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9929  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9930  within the timetable if required.
9931  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9932  services)
9933  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9934  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9935 
9936  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9937  text timetable file easier
9938 
9939  form:-
9940  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9941  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9942  then multiple entries, separated by commas, of the form:-
9943 
9944  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9945  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
9946  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
9947 
9948  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
9949  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
9950  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
9951 
9952  HH:MM;Command (cdt) }TimeCmd }
9953  HH:MM;Location (arr & dep) }TimeLoc }
9954  HH:MM;HH:MM;Location }TimeTimeLoc }
9955  HH:MM;pas;Location }PassTime }
9956  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
9957  HH:MM;Fer;set of allowable IDs }ExitRailway }
9958  Command (Frh only) }FinRemHere }
9959 
9960  R;mm;dd;nn. Repeat Repeat entry
9961 
9962  Formats:
9963 
9964  Command only: Frh
9965  Time;Command: cdt
9966  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
9967  Time;Command;2 Element IDs: Snt
9968  Time;Comand;n Element IDs: Fer
9969  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
9970  Time;Command;2 Element IDs;Headcode Snt-sh
9971  Time;Command;Location pas
9972  Time;Location Arr Dep
9973  Time;Time;Location Arr & dep together
9974 
9975  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
9976 
9977  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
9978  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
9979  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
9980 
9981  4 2x Linked entries, all shuttles:
9982 
9983  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
9984  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
9985  -> Remain Here (at finish location after all repeats)
9986  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
9987  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
9988 
9989  Allowable successors:-
9990 
9991  Successor state Type
9992 
9993  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
9994  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
9995  Sfs AtLoc )
9996  Sns AtLoc ) Start
9997  Sns-fsh AtLoc )
9998  Snt-sh AtLoc )
9999  Sns-sh AtLoc )
10000 
10001  pas Moving )
10002  jbo AtLoc )
10003  fsp AtLoc )
10004  rsp AtLoc ) Intermediate
10005  cdt AtLoc )
10006  TimeLoc arr Moving (bef), AtLoc (aft) )
10007  TimeLoc dep AtLoc (bef), Moving (aft) )
10008  TimeTimeLoc Moving )
10009 
10010  Fns Repeat/Nothing)
10011  Fjo Repeat/Nothing)
10012  Frh Repeat/Nothing)
10013  Fer Repeat/Nothing) Finish
10014  Frh-sh Repeat )
10015  Fns-sh Repeat )
10016  F-nshs Nothing )
10017 
10018  Descriptions:
10019  Snt New train
10020  Sfs New service from split
10021  Sns New service from another service
10022  Sns-fsh New non-repeating service from a shuttle service
10023  Snt-sh New shuttle train at a timetabled stop
10024  Sns-sh New shuttle service from a feeder service
10025 
10026  pas Pass
10027  jbo Be joined by another train
10028  fsp Front split
10029  rsp Rear split
10030  cdt Change direction of train
10031  TimeLoc arr Arrival
10032  TimeLoc dep Departure
10033  TimeTimeLoc Arrival and departure
10034 
10035  Fns Finish & form a new service
10036  Fjo Finish & join another train
10037  Frh Finish & remain here
10038  Fer Finish & exit railway
10039  Frh-sh Finish & repeat shuttle, finally remain here
10040  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
10041  F-nshs Finish & form a shuttle feeder service
10042 */
10043 
10044 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
10045 {
10046  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
10047  // a line that is too long; timetable containing too few lines; and timetable failed to open.
10048  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
10049  // new for v0.2b
10050  // compile ActiveTrackElementNameMap
10051  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
10052 
10054  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
10055  {
10056  // if((Track->TrackElementAt(, x).ActiveTrackElementName != "") && (Track->TrackElementAt(, x).TrackType != Continuation))
10058  == Track->ContinuationNameMap.end())
10059  {
10060  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
10061  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1035, x).ActiveTrackElementName;
10062  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
10063  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
10064  }
10065  }
10067  // end of new section
10068  std::ifstream TTBLFile(FileName, std::ios_base::binary);
10069 
10070  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
10071  if(TTBLFile.is_open())
10072  {
10073  char *TrainTimetableString = new char[10000];
10074  // enough for over 200 stations, should be adequate!
10075  bool EndOfFile = false;
10076  int Count = 0;
10077  // counts 'relevant' lines, i.e ignores any before the start time on its own line
10078  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10079  // delimiter is '\0' as it's an AnsiString
10080  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10081  // file empty - stores a null in 1st position if doesn't load any characters
10082  {
10083  // may still have eof even if read a line (no CRLF at end), and
10084  // if so need to process it
10085  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
10086  TTBLFile.close();
10087  delete[] TrainTimetableString;
10088  Utilities->CallLogPop(1611);
10089  return(false);
10090  }
10091  AnsiString OneLine(TrainTimetableString);
10092  bool FinalCallFalse = false;
10093  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10094  // get rid of lines before the start time
10095  {
10096  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
10097  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10098  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10099  // stores a null in 1st position if doesn't load any characters
10100  {
10101  // may still have eof even if read a line (no CRLF at end), and
10102  // if so need to process it
10103  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
10104  TTBLFile.close();
10105  delete[] TrainTimetableString;
10106  Utilities->CallLogPop(772);
10107  return(false);
10108  }
10109  OneLine = AnsiString(TrainTimetableString);
10110  }
10111  // here when have accepted the start time
10112  Count++; // increment past the start time
10113  while(!EndOfFile)
10114  {
10115  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10116  // get next line after start time
10117  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10118  // stores a null in 1st position if doesn't load any characters
10119  {
10120  // may still have eof even if read a line (no CRLF at end), and
10121  // if so need to process it
10122  EndOfFile = true;
10123  OneLine = "";
10124  }
10125  else
10126  {
10127  OneLine = AnsiString(TrainTimetableString);
10128  }
10129  if(OneLine.Length() > 9999)
10130  {
10131  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
10132  TTBLFile.close();
10133  delete[] TrainTimetableString;
10134  Utilities->CallLogPop(789);
10135  return(false);
10136  }
10137  bool FinalCallFalse = false;
10138  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10139  // false for FinalCall - just checking at this stage
10140  {
10141  TTBLFile.close();
10142  delete[] TrainTimetableString;
10143  Utilities->CallLogPop(770);
10144  return(false);
10145  }
10146  if(EndOfFile && (Count < 2))
10147  // Timetable must contain at least two relevant lines, one for start time and at least one train
10148  {
10149  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
10150  TTBLFile.close();
10151  delete[] TrainTimetableString;
10152  Utilities->CallLogPop(771);
10153  return(false);
10154  }
10155  Count++;
10156  }
10157  delete[] TrainTimetableString;
10158  TTBLFile.close();
10159  } // if(TTBLFile.is_open())
10160  else
10161  {
10162  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
10163  Utilities->CallLogPop(2154);
10164  return(false);
10165  }
10166  Utilities->CallLogPop(753);
10167  return(true);
10168 }
10169 
10170 // ---------------------------------------------------------------------------
10171 
10172 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
10173  bool CheckLocationsExistInRailway) // return true for success
10174 
10175 /* Format:
10176  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
10177  descriptive text or anything user wishes
10178  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
10179  be ignored) is taken as the timetable start time.
10180  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
10181  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
10182  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
10183  within the timetable if required.
10184  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
10185  services)
10186  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
10187  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
10188 
10189  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
10190  text timetable file easier
10191 
10192  form:-
10193  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
10194  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
10195  then multiple entries, separated by commas, of the form:-
10196 
10197  Format FormatType
10198  [W]HH:MM;Command (cdt) }TimeCmd }
10199  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
10200  [W]HH:MM;pas;Location }PassTime }
10201  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
10202  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
10203  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
10204  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
10205  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
10206  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
10207  [W]HH:MM;Fns-sh;Details }FSHNewService }
10208  [W]HH:MM;Location (arr & dep) }TimeLoc }
10209  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
10210  Command (Frh only) }FinRemHere }
10211 
10212  R;mm;dd;nn. Repeat Repeat entry
10213 
10214  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
10215  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
10216  at location; or (c) departure time if train already at location (including train started at location either as a new
10217  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
10218  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
10219  minutes, incremental train headcode last 2 digits, and number of repeats.
10220 
10221  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
10222  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
10223  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
10224  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
10225  (it's for a shuttle train to return to depot at end of services)
10226 
10227  Command/Location & details are as follows:-
10228 
10229  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
10230  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
10231  2E44 in its Sfs entry. All these are checked.
10232  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
10233 
10234  Start commands:-
10235  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
10236  with loc as a start entry can't have a location as details)
10237  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
10238  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
10239  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
10240  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
10241  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
10242  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
10243  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
10244  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
10245  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
10246  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
10247  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
10248 
10249  Intermediate commands:-
10250  Time - Location (TimeLoc), can be arrival or departure depending on context
10251  Time Time location (TimeTimeLoc), arrival and departure
10252  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
10253  pas (PassTime), Time;pas;Location
10254  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
10255  joining train's finish details must correspond or the file check will fail
10256  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
10257  new train - that train's starting information must correspond)
10258  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
10259  new train - that train's starting information must correspond)
10260  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
10261 
10262  Finish commands:-
10263  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
10264  creation)
10265  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
10266  shuttle headcode (no train creation)
10267  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
10268  may have to wait for it), details = new headcode (delete train)
10269  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
10270  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
10271  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
10272  here
10273  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
10274  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
10275 
10276  Repeat:-
10277  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
10278  headcodes - it is up to user to avoid duplicates if he/she wishes to.
10279 
10280  Checks carried out with error messages in this function:-
10281  At least one comma in a service line (it's based on a .csv file)
10282  No entries following train information;
10283  At least one comma in remainder after train information (i.e at least a start and a finish entry);
10284  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
10285  First entry not a start entry;
10286  Train information incomplete before a start entry;
10287  Entry follows a finish entry but doesn't begin with 'R';
10288  SplitEntry returns false in a finish entry - message repeats the entry for information;
10289  Last action entry isn't a finish entry.
10290 
10291  Function returns false with no message if:-
10292  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
10293  time is found at all then an error message is given in the calling function);
10294  SplitTrainInfo returns false (message given in called function);
10295  SplitRepeat returns false (message given in called function).
10296 */{
10297  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
10298  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
10299  TTrainDataEntry TempTrainDataEntry;
10300 
10301  EndOfFile = false;
10302  StripSpaces(0, OneLine);
10303  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
10304  // semicolons within the line
10305  ServiceReference = "";
10306  if(OneLine != "")
10307  {
10308  if(OneLine[1] != '*')
10309  {
10310  int SCPos = OneLine.Pos(';');
10311  if(SCPos == 0)
10312  {
10313  ServiceReference = OneLine.SubString(1, 8);
10314  }
10315  else
10316  {
10317  ServiceReference = OneLine.SubString(1, (SCPos - 1));
10318  }
10319  }
10320  }
10321  bool AllCommas = true;
10322 
10323  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
10324  {
10325  if(OneLine[x] != ',')
10326  {
10327  AllCommas = false;
10328  }
10329  }
10330  if(AllCommas || (OneLine == ""))
10331  {
10332  if(Count > 0)
10333  {
10334  EndOfFile = true;
10335  // returns true for a blank line - treated as end of file
10336  Utilities->CallLogPop(1018);
10337  return(true);
10338  }
10339  else // count == 0 so not yet found a start time, no message to be given
10340  {
10341  Utilities->CallLogPop(754);
10342  return(false);
10343  }
10344  }
10345  AnsiString First = "", Second = "", Third = "", Fourth = "";
10346  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
10347  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
10348  TDateTime StartTime(0);
10349  TNumList ExitList;
10350  bool Warning = false;
10351 
10352  if(Count == 0) // no start time found yet
10353  {
10354 /* dropped at v0.6b
10355  AnyHeadCodeValid = false;
10356  if(OneLine.SubString(6,5) == ";0000")
10357  {
10358  AnyHeadCodeValid = true;
10359  }
10360 */
10361  if(!CheckTimeValidity(0, OneLine, StartTime))
10362  {
10363  // no message is given for an invalid time as it's assumed to be an irrelevant line
10364  // if no start time is found at all then an error message is given in the calling function
10365  // AnyHeadCodeValid = false;
10366  Utilities->CallLogPop(755);
10367  return(false);
10368  }
10369  if(FinalCall) // here if start time valid
10370  {
10371  TTClockTime = StartTime;
10372  TimetableStartTime = StartTime;
10373  }
10374  }
10375  else
10376  {
10377  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
10378  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
10379  double MaxBrakeRate = 0;
10380  double PowerAtRail = 0;
10381  int SignallerSpeed = 0;
10382  if(OneLine[1] == '*')
10383  {
10384  Utilities->CallLogPop(1581);
10385  return(true);
10386  // ignore any line beginning with '*' but return true as there is no error
10387  }
10388  int Pos = OneLine.Pos(',');
10389  if(Pos == 0)
10390  {
10391  int SubStringLength = 20;
10392  if(OneLine.Length() < 20)
10393  {
10394  SubStringLength = OneLine.Length();
10395  }
10396  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
10397  Utilities->CallLogPop(766);
10398  return(false);
10399  }
10400  TrainInfoStr = OneLine.SubString(1, Pos - 1);
10401  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
10402  GiveMessages)) // error messages given in SplitTrainInfo
10403  {
10404  Utilities->CallLogPop(773);
10405  return(false);
10406  }
10407  if(FinalCall)
10408  {
10409  // store Train info - conversions done in SplitTrainInfo
10410  // only headcode mandatory for continuing services
10411  TempTrainDataEntry.HeadCode = HeadCode;
10412  TempTrainDataEntry.ServiceReference = HeadCode;
10413  TempTrainDataEntry.Description = Description;
10414  TempTrainDataEntry.StartSpeed = StartSpeed;
10415  TempTrainDataEntry.Mass = Mass;
10416  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
10417  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
10418  TempTrainDataEntry.PowerAtRail = PowerAtRail;
10419  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
10420  TTrainOperatingData TempTrainOperatingData;
10421  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
10422  }
10423  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
10424  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
10425  // so strip them off
10426  while(NewRemainder[NewRemainder.Length()] == ',')
10427  {
10428  if(NewRemainder.Length() > 1)
10429  {
10430  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
10431  }
10432  else
10433  {
10434  NewRemainder = "";
10435  break;
10436  }
10437  }
10438  // check if zero length & fail if so
10439  if(NewRemainder == "")
10440  {
10441  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
10442  Utilities->CallLogPop(769);
10443  return(false);
10444  }
10445  // now have one more entry than there are commas
10446  int CommaCount = 0;
10447  for(int x = 1; x < NewRemainder.Length() + 1; x++)
10448  {
10449  if(NewRemainder[x] == ',')
10450  {
10451  CommaCount++;
10452  }
10453  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
10454  if(CommaCount == 0)
10455  {
10456  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
10457  {
10458  int SubStringLength = 20;
10459  if(OneLine.Length() < 20)
10460  {
10461  SubStringLength = OneLine.Length();
10462  }
10463  TimetableMessage(GiveMessages,
10464  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
10465  OneLine.SubString(1, SubStringLength) + "'....");
10466  Utilities->CallLogPop(783);
10467  return(false);
10468  }
10469  }
10470  AnsiString OneEntry = "";
10471  TTimetableFormatType FormatType;
10472  TTimetableSequenceType SequenceType;
10473  TTimetableLocationType LocationType;
10474  TTimetableShuttleLinkType ShuttleLinkType;
10475  bool FinishFlag = false;
10476  for(int x = 0; x < CommaCount + 1; x++)
10477  {
10478  if((CommaCount == 0) || (x < CommaCount))
10479  // i.e. train entered under signaller control with no repeats, or entry is not the last,
10480  // in which case there's a comma & finish element or repeat still to come this entry could
10481  // be a finish but can't be a repeat
10482  {
10483  if(CommaCount == 0)
10484  {
10485  OneEntry = NewRemainder;
10486  NewRemainder = "";
10487  }
10488  else
10489  {
10490  Pos = NewRemainder.Pos(',');
10491  OneEntry = NewRemainder.SubString(1, Pos - 1);
10492  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
10493  }
10494  First = "";
10495  Second = "";
10496  Third = "";
10497  Fourth = "";
10498  RearStartOrRepeatMins = 0;
10499  FrontStartOrRepeatDigits = 0;
10500  NumberOfRepeats = 0;
10501  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10502  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10503  {
10504  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10505  Utilities->CallLogPop(756);
10506  return(false);
10507  }
10508  // check if warning for Frh or Fjo & reject
10509  if(Warning && (Second == "Frh"))
10510  {
10511  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
10512  Utilities->CallLogPop(1793);
10513  return(false);
10514  }
10515  if(Warning && (Second == "Fjo"))
10516  {
10517  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
10518  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
10519  Utilities->CallLogPop(1794);
10520  return(false);
10521  }
10522  if(x == 0) // should be start event
10523  {
10524  if(SequenceType != Start)
10525  {
10526  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
10527  Utilities->CallLogPop(784);
10528  return(false);
10529  }
10530  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
10531  {
10532  if(NewRemainder[1] != 'R')
10533  {
10534  TimetableMessage(GiveMessages,
10535  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
10536  OneEntry + "'");
10537  Utilities->CallLogPop(787);
10538  return(false);
10539  }
10540  }
10541  if((Second == "Snt") || (Second == "Snt-sh"))
10542  // need full train information including non-default values for at least HeadCode, Description,
10543  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
10544  {
10545  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
10546  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
10547  {
10548  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
10549  OneEntry + "'");
10550  Utilities->CallLogPop(1783);
10551  return(false);
10552  }
10553  }
10554  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
10555  // service continuation - need at least non-default value for HeadCode
10556  {
10557  if(HeadCode == "")
10558  {
10559  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10560  OneEntry + "'");
10561  Utilities->CallLogPop(788);
10562  return(false);
10563  }
10564  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
10565  {
10566  TimetableMessage(GiveMessages,
10567  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10568  OneEntry + "'");
10569  Utilities->CallLogPop(843);
10570  return(false);
10571  }
10572  }
10573  }
10574  if(SequenceType == Finish)
10575  {
10576  FinishFlag = true;
10577  // marker for only permitted additional entry being a repeat, only needed if the
10578  // finish entry is not the last entry
10579  }
10580  if(FinalCall)
10581  {
10582  // interpret & add to ActionVector
10583  TDateTime TempTime;
10584  TActionVectorEntry ActionVectorEntry;
10585  ActionVectorEntry.FormatType = FormatType;
10586  ActionVectorEntry.LocationType = LocationType;
10587  ActionVectorEntry.SequenceType = SequenceType;
10588  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10589  ActionVectorEntry.Warning = Warning;
10590  if(FormatType == TimeLoc)
10591  {
10592  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
10593  {
10594  ;
10595  } // these will all be true as final call
10596 
10597  ActionVectorEntry.LocationName = Second;
10598  }
10599  else if(FormatType == PassTime) // new
10600  {
10601  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
10602  {
10603  ;
10604  }
10605  ActionVectorEntry.Command = Second;
10606  ActionVectorEntry.LocationName = Third;
10607  }
10608  else if(FormatType == TimeTimeLoc)
10609  {
10610  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
10611  {
10612  ;
10613  }
10614  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
10615  {
10616  ;
10617  }
10618  ActionVectorEntry.LocationName = Third;
10619  }
10620  else if(FormatType == TimeCmd)
10621  {
10622  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
10623  {
10624  ;
10625  }
10626  ActionVectorEntry.Command = Second;
10627  }
10628  else if(FormatType == ExitRailway)
10629  {
10630  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
10631  {
10632  ;
10633  }
10634  ActionVectorEntry.Command = Second;
10635  ActionVectorEntry.ExitList = ExitList;
10636  }
10637  else if(FormatType == StartNew)
10638  {
10639  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
10640  {
10641  ;
10642  }
10643  ActionVectorEntry.Command = Second;
10644  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10645  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10646  if(Fourth == 'S')
10647  {
10648  ActionVectorEntry.SignallerControl = true;
10649  }
10650  }
10651  else if(FormatType == SNTShuttle)
10652  {
10653  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
10654  {
10655  ;
10656  }
10657  ActionVectorEntry.Command = Second;
10658  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10659  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10660  ActionVectorEntry.OtherHeadCode = Fourth;
10661  }
10662  else if(FormatType == SNSShuttle)
10663  {
10664  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
10665  {
10666  ;
10667  }
10668  ActionVectorEntry.Command = Second;
10669  ActionVectorEntry.OtherHeadCode = Third;
10670  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10671  }
10672  else if(FormatType == TimeCmdHeadCode)
10673  {
10674  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
10675  {
10676  ;
10677  }
10678  ActionVectorEntry.Command = Second;
10679  ActionVectorEntry.OtherHeadCode = Third;
10680  }
10681  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
10682  {
10683  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
10684  {
10685  ;
10686  }
10687  ActionVectorEntry.Command = Second;
10688  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10689  }
10690  else if(FormatType == FSHNewService)
10691  {
10692  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
10693  {
10694  ;
10695  }
10696  ActionVectorEntry.Command = Second;
10697  ActionVectorEntry.OtherHeadCode = Third;
10698  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10699  }
10700  else if(FormatType == FinRemHere)
10701  {
10702  ActionVectorEntry.Command = Second;
10703  }
10704  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10705  }
10706  }
10707  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
10708  {
10709  OneEntry = NewRemainder;
10710  First = "";
10711  Second = "";
10712  Third = "";
10713  Fourth = "";
10714  RearStartOrRepeatMins = 0;
10715  FrontStartOrRepeatDigits = 0;
10716  NumberOfRepeats = 0;
10717  if((FinishFlag) && (OneEntry[1] != 'R'))
10718  // already had a finish entry
10719  {
10720  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
10721  Utilities->CallLogPop(79);
10722  return(false);
10723  }
10724  if(OneEntry[1] != 'R') // must be finish
10725  {
10726  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10727  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10728  {
10729  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10730  Utilities->CallLogPop(757);
10731  return(false);
10732  }
10733  if(SequenceType != Finish)
10734  {
10735  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
10736  Utilities->CallLogPop(785);
10737  return(false);
10738  }
10739  if(FinalCall)
10740  {
10741  // interpret & add to ActionVector
10742  TDateTime TempTime;
10743  TActionVectorEntry ActionVectorEntry;
10744  ActionVectorEntry.FormatType = FormatType;
10745  ActionVectorEntry.LocationType = LocationType;
10746  ActionVectorEntry.SequenceType = SequenceType;
10747  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10748  ActionVectorEntry.Warning = Warning;
10749  if(FormatType == TimeCmd)
10750  {
10751  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
10752  {
10753  ;
10754  }
10755  ActionVectorEntry.Command = Second;
10756  }
10757  else if(FormatType == TimeCmdHeadCode)
10758  {
10759  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
10760  {
10761  ;
10762  }
10763  ActionVectorEntry.Command = Second;
10764  ActionVectorEntry.OtherHeadCode = Third;
10765  }
10766  else if(FormatType == FNSNonRepeatToShuttle)
10767  {
10768  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
10769  {
10770  ;
10771  }
10772  ActionVectorEntry.Command = Second;
10773  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10774  }
10775  else if(FormatType == FSHNewService)
10776  {
10777  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
10778  {
10779  ;
10780  }
10781  ActionVectorEntry.Command = Second;
10782  ActionVectorEntry.OtherHeadCode = Third;
10783  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10784  }
10785  else if(FormatType == ExitRailway)
10786  {
10787  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
10788  {
10789  ;
10790  }
10791  ActionVectorEntry.Command = Second;
10792  ActionVectorEntry.ExitList = ExitList;
10793  }
10794  else if(FormatType == FinRemHere)
10795  {
10796  ActionVectorEntry.Command = Second;
10797  }
10798  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10799  }
10800  }
10801  else // repeat
10802  {
10803  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
10804  {
10805  Utilities->CallLogPop(786);
10806  // error messages given in SplitRepeat
10807  return(false);
10808  }
10809  if(FinalCall)
10810  {
10811  TActionVectorEntry ActionVectorEntry;
10812  ActionVectorEntry.FormatType = Repeat;
10813  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
10814  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
10815  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
10816  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10817  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10818  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
10819  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10820  }
10821  }
10822  }
10823  }
10824  if(FinalCall)
10825  {
10826  TrainDataVector.push_back(TempTrainDataEntry);
10827  }
10828  }
10829  Utilities->CallLogPop(80);
10830  return(true);
10831 }
10832 
10833 // ---------------------------------------------------------------------------
10834 
10835 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
10836 {
10837  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
10838  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
10839  {
10840  Utilities->CallLogPop(1890);
10841  return(false);
10842  }
10843  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
10844  {
10845  Utilities->CallLogPop(1891);
10846  return(false);
10847  }
10848  Utilities->CallLogPop(1892);
10849  return(true);
10850 }
10851 
10852 // ---------------------------------------------------------------------------
10853 
10854 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
10855 // 1st 5 chars must be HH:MM, anything else will be ignored
10856 {
10857  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
10858  if(TimeStr.Length() < 5)
10859  {
10860  Utilities->CallLogPop(926);
10861  return(false);
10862  }
10863  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
10864  {
10865  Utilities->CallLogPop(927);
10866  return(false);
10867  }
10868  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
10869  {
10870  Utilities->CallLogPop(928);
10871  return(false);
10872  }
10873  if(TimeStr[3] != ':')
10874  {
10875  Utilities->CallLogPop(929);
10876  return(false);
10877  }
10878  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
10879  {
10880  Utilities->CallLogPop(930);
10881  return(false);
10882  }
10883  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
10884  {
10885  Utilities->CallLogPop(931);
10886  return(false);
10887  }
10888  while(TimeStr.Length() > 5)
10889  {
10890  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
10891  }
10892  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
10893  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
10894 
10895  if((WholeHours + FracHour) >= 95.98334)
10896  {
10897  Utilities->CallLogPop(1817);
10898  return(false); // > 95h 59m
10899  }
10900  Time = TDateTime((WholeHours + FracHour) / 24);
10901  Utilities->CallLogPop(932);
10902  return(true);
10903 }
10904 
10905 // ---------------------------------------------------------------------------
10906 
10907 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
10908  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
10909  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
10910 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
10911  Return false for failure.
10912  See description above under ProcessOneTimetableLinefor details of train action entries
10913  NB all types set except LocationType for Sns as may be located or not
10914 */{
10915  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
10916  Warning = false;
10917  TDateTime TempTime;
10918 
10919  if(OneEntry.Length() > 0)
10920  {
10921  if(OneEntry[1] == 'W') // warning
10922  {
10923  Warning = true;
10924  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
10925  // strip it off
10926  }
10927  }
10928  if(OneEntry == "Frh")
10929  {
10930  FormatType = FinRemHere;
10931  SequenceType = Finish;
10932  LocationType = AtLocation;
10933  ShuttleLinkType = NotAShuttleLink;
10934  Second = "Frh";
10935  Utilities->CallLogPop(1016);
10936  return(true);
10937  }
10938  if(OneEntry.Length() < 7)
10939  {
10940  Utilities->CallLogPop(907);
10941  return(false); // 'HH:MM;' + at least a one-letter location name
10942  }
10943  int Pos = OneEntry.Pos(';'); // first segment delimiter
10944 
10945  if(Pos != 6)
10946  {
10947  Utilities->CallLogPop(908);
10948  return(false);
10949  // no delimiter or delimiter not in position 6, has to be a time so fail
10950  }
10951  First = OneEntry.SubString(1, 5); // has to be a time
10952  if(!CheckTimeValidity(16, First, TempTime))
10953  {
10954  Utilities->CallLogPop(909);
10955  return(false);
10956  }
10957  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
10958 
10959  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
10960  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
10961  {
10962  if(Remainder.Length() < 7)
10963  {
10964  Utilities->CallLogPop(910);
10965  return(false); // 'HH:MM;' + at least a one-letter location name
10966  }
10967  Pos = Remainder.Pos(';'); // second segment delimiter
10968  if(Pos == 0)
10969  {
10970  Utilities->CallLogPop(911);
10971  return(false);
10972  // no delimiter, has to be one between departure time & location
10973  }
10974  Second = Remainder.SubString(1, 5); // has to be a time
10975  if(!CheckTimeValidity(15, Second, TempTime))
10976  {
10977  Utilities->CallLogPop(912);
10978  return(false);
10979  }
10980  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10981  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
10982  {
10983  Utilities->CallLogPop(913);
10984  return(false);
10985  }
10986  FormatType = TimeTimeLoc;
10987  SequenceType = Intermediate;
10988  LocationType = AtLocation;
10989  ShuttleLinkType = NotAShuttleLink;
10990  Utilities->CallLogPop(914);
10991  return(true);
10992  }
10993  Pos = Remainder.Pos(';'); // second segment delimiter
10994  if(Pos == 0) // no third segment so second must be a location, or cdt
10995  {
10996  Second = Remainder;
10997  if(Second == "cdt")
10998  {
10999  FormatType = TimeCmd;
11000  ShuttleLinkType = NotAShuttleLink;
11001  LocationType = AtLocation;
11002  SequenceType = Intermediate;
11003  Utilities->CallLogPop(915);
11004  return(true);
11005  }
11006  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
11007  {
11008  Utilities->CallLogPop(916);
11009  return(false);
11010  }
11011  else
11012  {
11013  FormatType = TimeLoc;
11014  LocationType = AtLocation;
11015  SequenceType = Intermediate;
11016  ShuttleLinkType = NotAShuttleLink;
11017  Utilities->CallLogPop(917);
11018  return(true);
11019  }
11020  }
11021  // here if second segment is a command, with a third & maybe fourth segments as details
11022  if((Pos != 4) && (Pos != 7) && (Pos != 8))
11023  {
11024  Utilities->CallLogPop(918);
11025  return(false);
11026  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
11027  }
11028  Second = Remainder.SubString(1, Pos - 1); // command
11029 
11030  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11031  // details
11032  Pos = Remainder.Pos(';'); // third segment delimiter
11033  if(Pos == 0)
11034  {
11035  Third = Remainder;
11036  }
11037  else
11038  {
11039  Third = Remainder.SubString(1, Pos - 1);
11040  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11041  }
11042  if((Second == "Snt") || (Second == "Snt-sh"))
11043  // third has to be 2 element idents with a space between
11044  {
11045  int SpacePos = Third.Pos(' ');
11046  if(SpacePos == 0)
11047  {
11048  Utilities->CallLogPop(919);
11049  return(false); // no space
11050  }
11051  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
11052  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
11053  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
11054  if(CheckLocationsExistInRailway)
11055  {
11056  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
11057  {
11058  Utilities->CallLogPop(920);
11059  return(false);
11060  }
11061  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
11062  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
11063  }
11064  if(Second == "Snt")
11065  {
11066  FormatType = StartNew;
11067  SequenceType = Start;
11068  LocationType = NoLocation;
11069  // can't be set until know whether located or not - done in SecondPassActions
11070  ShuttleLinkType = NotAShuttleLink;
11071  }
11072  else // Snt-sh
11073  {
11074  FormatType = SNTShuttle;
11075  LocationType = AtLocation;
11076  SequenceType = Start;
11077  ShuttleLinkType = ShuttleLink;
11078  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
11079  {
11080  Utilities->CallLogPop(1038);
11081  return(false);
11082  }
11083  }
11084  Utilities->CallLogPop(921);
11085  return(true);
11086  }
11087  if(Second == "Sns-sh") // third & fourth have to be headcodes
11088  {
11089  FormatType = SNSShuttle;
11090  LocationType = AtLocation;
11091  SequenceType = Start;
11092  ShuttleLinkType = ShuttleLink;
11093  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
11094  {
11095  Utilities->CallLogPop(1039);
11096  return(false);
11097  }
11098  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
11099  {
11100  Utilities->CallLogPop(1040);
11101  return(false);
11102  }
11103  Utilities->CallLogPop(1041);
11104  return(true);
11105  }
11106  if(Second == "F-nshs")
11107  {
11108  FormatType = FNSNonRepeatToShuttle;
11109  LocationType = AtLocation;
11110  SequenceType = Finish;
11111  ShuttleLinkType = ShuttleLink;
11112  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
11113  {
11114  Utilities->CallLogPop(1047);
11115  return(false);
11116  }
11117  Utilities->CallLogPop(1048);
11118  return(true);
11119  }
11120  if(Second == "Sns-fsh")
11121  {
11122  FormatType = SNSNonRepeatFromShuttle;
11123  LocationType = AtLocation;
11124  SequenceType = Start;
11125  ShuttleLinkType = ShuttleLink;
11126  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
11127  {
11128  Utilities->CallLogPop(1098);
11129  return(false);
11130  }
11131  Utilities->CallLogPop(1099);
11132  return(true);
11133  }
11134  if(Second == "Fns-sh") // third & fourth have to be headcodes
11135  {
11136  FormatType = FSHNewService;
11137  LocationType = AtLocation;
11138  SequenceType = Finish;
11139  ShuttleLinkType = ShuttleLink;
11140  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
11141  {
11142  Utilities->CallLogPop(1050);
11143  return(false);
11144  }
11145  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
11146  {
11147  Utilities->CallLogPop(1051);
11148  return(false);
11149  }
11150  Utilities->CallLogPop(1052);
11151  return(true);
11152  }
11153  // new segment for 'pas'
11154  if(Second == "pas") // third has to be a location
11155  {
11156  FormatType = PassTime;
11157  LocationType = EnRoute;
11158  SequenceType = Intermediate;
11159  ShuttleLinkType = NotAShuttleLink;
11160  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
11161  {
11162  Utilities->CallLogPop(1515);
11163  return(false);
11164  }
11165  Utilities->CallLogPop(1516);
11166  return(true);
11167  }
11168  // new segment for revised 'Fer'
11169  if(Second == "Fer")
11170  // third has to be a set of IDs separated by spaces, and at least 1
11171  {
11172  FormatType = ExitRailway;
11173  LocationType = EnRoute;
11174  SequenceType = Finish;
11175  ShuttleLinkType = NotAShuttleLink;
11176  if(CheckLocationsExistInRailway)
11177  {
11178  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
11179  {
11180  Utilities->CallLogPop(1519);
11181  return(false);
11182  }
11183  }
11184  Utilities->CallLogPop(1520);
11185  return(true);
11186  }
11187  // all remainder must be TimeCmdHeadCode types to be valid
11188  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
11189  (Second != "Frh-sh"))
11190  {
11191  Utilities->CallLogPop(922);
11192  return(false); // all TimeCmdHeadCode types
11193  }
11194  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
11195  {
11196  Utilities->CallLogPop(923);
11197  return(false);
11198  }
11199  FormatType = TimeCmdHeadCode;
11200  LocationType = AtLocation;
11201  if(Second == "Frh-sh")
11202  {
11203  ShuttleLinkType = ShuttleLink;
11204  }
11205  else
11206  {
11207  ShuttleLinkType = NotAShuttleLink;
11208  }
11209  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
11210  {
11211  SequenceType = Finish;
11212  }
11213  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
11214  {
11215  SequenceType = Intermediate;
11216  }
11217  if((Second == "Sfs") || (Second == "Sns"))
11218  {
11219  SequenceType = Start;
11220  }
11221  Utilities->CallLogPop(924);
11222  return(true);
11223 }
11224 
11225 // ---------------------------------------------------------------------------
11226 
11227 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
11228 {
11229  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
11230  // and contains no special characters
11231  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
11232  if(LocStr == "")
11233  {
11234  Utilities->CallLogPop(1353);
11235  return(false); // has to have at least one character
11236  }
11237  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
11238  {
11239  Utilities->CallLogPop(1354);
11240  return(false); // can't begin with a number
11241  }
11242  for(int x = 1; x < LocStr.Length() + 1; x++)
11243  {
11244  if(LocStr[x] < ' ')
11245  {
11246  Utilities->CallLogPop(1355);
11247  return(false); // contains a special character
11248  }
11249  if(LocStr[x] > 'z')
11250  {
11251  Utilities->CallLogPop(1356);
11252  return(false); // contains a character outside the standard ASCII set
11253  }
11254  }
11255  // check exists in railway location list if CheckLocationsExistInRailway is true
11256  if(CheckLocationsExistInRailway)
11257  {
11258  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
11259  {
11260  TimetableMessage(GiveMessages, "Location name '" + LocStr +
11261  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
11262  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
11263  "that includes a continuation will not be valid.");
11264  Utilities->CallLogPop(1357);
11265  return(false);
11266  }
11267  }
11268  Utilities->CallLogPop(1358);
11269  return(true);
11270 }
11271 
11272 // ---------------------------------------------------------------------------
11273 
11274 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
11275 {
11276  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
11277  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
11278  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
11279  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
11280  HeadCode);
11281  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
11282  {
11283  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
11284  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Sns, Snt-sh, Sns-sh, Fns, Fns-sh or Frh-sh");
11285  Utilities->CallLogPop(1359);
11286  return(false);
11287  }
11288  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
11289  for(int x = 1; x < (HeadCode.Length() + 1); x++)
11290  {
11291  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
11292  {
11293  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
11294  Utilities->CallLogPop(1895);
11295  return(false);
11296  }
11297  }
11298  // secondly ensure the true Headcode only has letters or digits
11299  for(int x = 3; x >= 0; x--)
11300  {
11301  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
11302  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
11303  {
11304  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
11305  Utilities->CallLogPop(1790);
11306  return(false);
11307  }
11308  }
11309  Utilities->CallLogPop(1364);
11310  return(true);
11311 }
11312 
11313 // ---------------------------------------------------------------------------
11314 
11315 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
11316 // set of legitimate track element IDs, separated by spaces, and at least 1 present
11317 {
11318  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSetOfIDs," + IDSet);
11319  ExitList.clear();
11320  AnsiString CurrentID = "";
11321 
11322  if(IDSet.Length() == 0)
11323  {
11324  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
11325  Utilities->CallLogPop(1521);
11326  return(false);
11327  }
11328  for(int x = 1; x <= IDSet.Length(); x++)
11329  {
11330  char C = IDSet[x];
11331  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
11332  {
11333  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
11334  Utilities->CallLogPop(1522);
11335  return(false);
11336  }
11337  }
11338  int Pos = IDSet.Pos(' '); // look for the first space
11339 
11340  while(true)
11341  {
11342  if(Pos == 0)
11343  {
11344  CurrentID = IDSet;
11345  IDSet = "";
11346  }
11347  else
11348  {
11349  CurrentID = IDSet.SubString(1, Pos - 1);
11350  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
11351  }
11352  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
11353  if(VecPos == -1)
11354  {
11355  Utilities->CallLogPop(1523);
11356  return(false); // messages given in GetTrackVectorPositionFromString
11357  }
11358  else
11359  {
11360  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
11361  {
11362  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
11363  Utilities->CallLogPop(1524);
11364  return(false);
11365  }
11366  else
11367  {
11368  // first check for duplicates
11369  if(!ExitList.empty())
11370  {
11371  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
11372  {
11373  if(*ELIT == VecPos)
11374  {
11375  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
11376  Utilities->CallLogPop(1532);
11377  return(false);
11378  }
11379  }
11380  }
11381  // of OK add it to the list
11382  ExitList.push_back(VecPos);
11383  }
11384  }
11385  if(IDSet == "")
11386  {
11387  Utilities->CallLogPop(1525);
11388  return(true);
11389  }
11390  else
11391  {
11392  Pos = IDSet.Pos(' '); // look for the next space
11393  }
11394  } // while(true)
11395 }
11396 
11397 // ---------------------------------------------------------------------------
11398 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
11399  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
11400 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
11401 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
11402 // of each item
11403 {
11404  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
11405  int Pos = 0;
11406  AnsiString Remainder = "";
11407  int SemiColonCount = 0;
11408 
11409  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
11410  {
11411  if(TrainInfoStr[x] == ';')
11412  {
11413  SemiColonCount++;
11414  }
11415  }
11416  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
11417  {
11418  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
11419  "'. Should be headcode + optional description for a continuing service;" +
11420  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
11421  Utilities->CallLogPop(880);
11422  return(false);
11423  }
11424  if(SemiColonCount == 0)
11425  {
11426  HeadCode = TrainInfoStr;
11427  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
11428  {
11429  Utilities->CallLogPop(881);
11430  return(false);
11431  }
11432  Utilities->CallLogPop(882);
11433  return(true);
11434  }
11435  if(SemiColonCount == 1) // headcode & description only
11436  {
11437  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11438  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11439  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11440  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
11441  {
11442  Utilities->CallLogPop(883);
11443  return(false);
11444  }
11445  if(Description == "")
11446  {
11447  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11448  Utilities->CallLogPop(884);
11449  return(false);
11450  }
11451  if(Description.Length() > 60)
11452  {
11453  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11454  Utilities->CallLogPop(1157);
11455  return(false);
11456  }
11457  for(int x = 1; x < Description.Length() + 1; x++)
11458  {
11459  if((Description[x] < ' ') || (Description[x] > '~'))
11460  {
11461  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11462  Utilities->CallLogPop(885);
11463  return(false);
11464  }
11465  }
11466  Utilities->CallLogPop(886);
11467  return(true);
11468  }
11469  // if here must have 6 or 7 semicolons
11470  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11471  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11472  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11473  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
11474  {
11475  Utilities->CallLogPop(887);
11476  return(false);
11477  }
11478  Pos = Remainder.Pos(';'); // 2nd delimiter
11479  Description = Remainder.SubString(1, Pos - 1);
11480  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11481  if(Description == "")
11482  {
11483  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11484  Utilities->CallLogPop(888);
11485  return(false);
11486  }
11487  if(Description.Length() > 60)
11488  {
11489  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11490  Utilities->CallLogPop(1158);
11491  return(false);
11492  }
11493  for(int x = 1; x < Description.Length() + 1; x++)
11494  {
11495  if((Description[x] < ' ') || (Description[x] > 126))
11496  {
11497  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11498  Utilities->CallLogPop(889);
11499  return(false);
11500  }
11501  }
11502  Pos = Remainder.Pos(';'); // 3rd delimiter
11503  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
11504 
11505  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11506  if(StartSpeedStr == "")
11507  {
11508  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
11509  Utilities->CallLogPop(890);
11510  return(false);
11511  }
11512  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
11513  {
11514  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
11515  {
11516  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
11517  Utilities->CallLogPop(891);
11518  return(false);
11519  }
11520  }
11521  StartSpeed = StartSpeedStr.ToInt();
11522  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11523  {
11524  StartSpeed = TTrain::MaximumSpeedLimit;
11525  if(!SSHigh) // added at v2.4.0
11526  {
11527  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11528  SSHigh = true;
11529  }
11530  }
11531  Pos = Remainder.Pos(';'); // 4th delimiter
11532  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
11533 
11534  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11535  if(MaxRunningSpeedStr == "")
11536  {
11537  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
11538  Utilities->CallLogPop(892);
11539  return(false);
11540  }
11541  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
11542  {
11543  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
11544  {
11545  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
11546  Utilities->CallLogPop(893);
11547  return(false);
11548  }
11549  }
11550  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
11551  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11552  {
11553  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
11554  if(!MRSHigh) // added at v2.4.0
11555  {
11556  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11557  MRSHigh = true;
11558  }
11559  }
11560  if(MaxRunningSpeed < 10)
11561  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11562  {
11563  MaxRunningSpeed = 10;
11564  if(!MRSLow) // added at v2.4.0
11565  {
11566  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11567  MRSLow = true;
11568  }
11569  }
11570  Pos = Remainder.Pos(';'); // 5th delimiter
11571  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
11572 
11573  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11574  if(MassStr == "")
11575  {
11576  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
11577  Utilities->CallLogPop(895);
11578  return(false);
11579  }
11580  for(int x = 1; x < MassStr.Length() + 1; x++)
11581  {
11582  if((MassStr[x] < '0') || (MassStr[x] > '9'))
11583  {
11584  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
11585  Utilities->CallLogPop(896);
11586  return(false);
11587  }
11588  }
11589  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
11590  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
11591  {
11592  Mass = TTrain::MaximumMassLimit;
11593  if(!MassHigh) // added at v2.4.0
11594  {
11595  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
11596  MassHigh = true;
11597  }
11598  }
11599  if(Mass == 0)
11600  {
11601  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
11602  Utilities->CallLogPop(897);
11603  return(false);
11604  }
11605  Pos = Remainder.Pos(';'); // 6th delimiter
11606  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
11607 
11608  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11609  if(MaxBrakeForceStr == "")
11610  {
11611  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
11612  Utilities->CallLogPop(898);
11613  return(false);
11614  }
11615  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
11616  {
11617  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
11618  {
11619  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
11620  Utilities->CallLogPop(899);
11621  return(false);
11622  }
11623  }
11624  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
11625 
11626  // convert to kg force
11627  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
11628  {
11629  MaxBrakeForce = Mass;
11630  if(!BFHigh) // added at v2.4.0
11631  {
11632  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
11633  BFHigh = true;
11634  }
11635  }
11636  if((MaxBrakeForce / Mass) < 0.01)
11637  {
11638  MaxBrakeForce = Mass * 0.01;
11639  if(!BFLow) // added at v2.4.0
11640  {
11641  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
11642  BFLow = true;
11643  }
11644  }
11645  // convert to m/s/s
11646  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
11647  // now may have just a power entry or power and signaller max. speed
11648  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
11649 
11650  if(SemiColonCount == 6)
11651  {
11652  GrossPowerStr = Remainder;
11653  SignallerSpeedStr = "30"; // default value
11654  }
11655  else // must be 7
11656  {
11657  Pos = Remainder.Pos(';'); // 7th delimiter
11658  GrossPowerStr = Remainder.SubString(1, Pos - 1);
11659  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11660  }
11661  // deal with GrossPower
11662  if(GrossPowerStr == "")
11663  {
11664  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
11665  Utilities->CallLogPop(901);
11666  return(false);
11667  }
11668  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
11669  {
11670  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
11671  {
11672  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
11673  Utilities->CallLogPop(902);
11674  return(false);
11675  }
11676  }
11677 
11678  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
11679 
11680  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
11681  {
11682  GrossPower = TTrain::MaximumPowerLimit;
11683  if(!PwrHigh)
11684  {
11685  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
11686  PwrHigh = true;
11687  }
11688  }
11689  else if(GrossPower == 0) // changed at v2.4.0
11690  {
11691  GrossPower = 0.1;
11692  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
11693  }
11694  else if((GrossPower > 0) && (GrossPower < 10000))
11695  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
11696  {
11697  GrossPower = 10000;
11698  }
11699  PowerAtRail = GrossPower * 0.8;
11700  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
11701 
11702  // deal with SignallerSpeed
11703  if(SignallerSpeedStr == "")
11704  {
11705  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
11706  Utilities->CallLogPop(1771);
11707  return(false);
11708  }
11709  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
11710  {
11711  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
11712  {
11713  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
11714  Utilities->CallLogPop(1769);
11715  return(false);
11716  }
11717  }
11718  SignallerSpeed = SignallerSpeedStr.ToInt();
11719  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
11720  {
11721  SignallerSpeed = TTrain::MaximumSpeedLimit;
11722  if(!SigSHigh)
11723  {
11724  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11725  SigSHigh = true;
11726  }
11727  }
11728  if(SignallerSpeed < 10)
11729  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11730  {
11731  SignallerSpeed = 10;
11732  if(!SigSLow)
11733  {
11734  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11735  SigSLow = true;
11736  }
11737  // Utilities->CallLogPop(1770);
11738  // return false;
11739  }
11740  Utilities->CallLogPop(904);
11741  return(true);
11742 }
11743 
11744 // ---------------------------------------------------------------------------
11745 
11746 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
11747  bool GiveMessages)
11748 {
11749  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
11750  // function checks validity of each item and returns false for error
11751  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
11752  if(OneEntry.Length() < 7)
11753  {
11754  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11755  Utilities->CallLogPop(865);
11756  return(false);
11757  }
11758  int SemiColonCount = 0;
11759 
11760  for(int x = 1; x < OneEntry.Length() + 1; x++)
11761  {
11762  if(OneEntry[x] == ';')
11763  {
11764  SemiColonCount++;
11765  }
11766  }
11767  if(SemiColonCount != 3)
11768  {
11769  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11770  Utilities->CallLogPop(866);
11771  return(false);
11772  }
11773  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
11774  {
11775  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11776  Utilities->CallLogPop(867);
11777  return(false);
11778  }
11779  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
11780  // strip off R;
11781 
11782  int Pos = 0;
11783 
11784  Pos = Remainder.Pos(';');
11785  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
11786 
11787  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11788  if(MinutesStr == "")
11789  {
11790  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
11791  Utilities->CallLogPop(868);
11792  return(false);
11793  }
11794  if(MinutesStr.Length() > 3)
11795  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
11796  {
11797  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
11798  Utilities->CallLogPop(2119);
11799  return(false);
11800  }
11801  for(int x = 1; x < MinutesStr.Length() + 1; x++)
11802  {
11803  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
11804  {
11805  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
11806  Utilities->CallLogPop(869);
11807  return(false);
11808  }
11809  }
11810  RearStartOrRepeatMins = MinutesStr.ToInt();
11811  if(RearStartOrRepeatMins == 0)
11812  {
11813  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
11814  Utilities->CallLogPop(870);
11815  return(false);
11816  }
11817  Pos = Remainder.Pos(';');
11818  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
11819 
11820  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11821  if(DigitsStr == "")
11822  {
11823  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
11824  Utilities->CallLogPop(871);
11825  return(false);
11826  }
11827  for(int x = 1; x < DigitsStr.Length() + 1; x++)
11828  {
11829  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
11830  {
11831  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
11832  Utilities->CallLogPop(872);
11833  return(false);
11834  }
11835  }
11836  if(DigitsStr.Length() > 2)
11837  {
11838  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
11839  Utilities->CallLogPop(873);
11840  return(false);
11841  }
11842  FrontStartOrRepeatDigits = DigitsStr.ToInt();
11843 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
11844  route rather than the service
11845  if(FrontStartOrRepeatDigits == 0)
11846  {
11847  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
11848  Utilities->CallLogPop(874);
11849  return false;
11850  }
11851 */
11852  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
11853  // new for v0.6b for unrestricted headcodes
11854  {
11855  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
11856  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
11857  Utilities->CallLogPop(1889);
11858  return(false);
11859  }
11860  AnsiString NumberStr = Remainder;
11861 
11862  if(NumberStr == "")
11863  {
11864  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
11865  Utilities->CallLogPop(875);
11866  return(false);
11867  }
11868  if(NumberStr.Length() > 4)
11869  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
11870  {
11871  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
11872  Utilities->CallLogPop(2118);
11873  return(false);
11874  }
11875  for(int x = 1; x < NumberStr.Length() + 1; x++)
11876  {
11877  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
11878  // catches negative numbers
11879  {
11880  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
11881  Utilities->CallLogPop(876);
11882  return(false);
11883  }
11884  }
11885  NumberOfRepeats = NumberStr.ToInt();
11886  if(NumberOfRepeats == 0)
11887  {
11888  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
11889  Utilities->CallLogPop(877);
11890  return(false);
11891  }
11892  Utilities->CallLogPop(878);
11893  return(true);
11894 }
11895 
11896 // ---------------------------------------------------------------------------
11897 
11898 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag) //TwoLocationFlag added at v2.9.1
11899 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
11900  vector rather than the timetable
11901  Note also that for unlocated Snt entries the LocationType hasn't yet been set
11902 
11903  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
11904 
11905  For info:-
11906  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
11907  {
11908  public:
11909  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled event entries, null
11911  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
11912  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
11913  int NumberOfRepeats; ///< the number of repeating services
11914  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
11916  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
11918  TNumList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
11919  TTimetableFormatType FormatType; ///< defines the timetable action type
11920  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
11921  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
11922  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
11923  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
11925  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
11927 
11928  // inline function
11929 
11931  TActionVectorEntry() {
11932  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
11933  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
11934  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
11935  Warning = false; SignallerControl = false;
11936  }
11937  };
11938 
11939  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
11940 
11941  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
11942  {
11943  public:
11944  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
11947  double MaxBrakeRate; ///< in metres/sec/sec
11948  double MaxRunningSpeed; ///< in km/h
11949  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
11950  int Mass; ///< in kg
11951  int NumberOfTrains; ///< number of repeats + 1
11952  int SignallerSpeed; ///< in km/h for use when under signaller control
11953  int StartSpeed; ///< in km/h
11954  TActionVector ActionVector; ///< all the actions for the train
11955  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
11956 
11957  //inline function
11958 
11960  TTrainDataEntry()
11961  {
11962  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
11963  }
11964  };
11965 
11966  Allowable successors:-
11967  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
11968  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11969  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11970  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11971  set location, else fails)
11972  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11973  set location, else fails)
11974  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11975  set location, else fails)
11976  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11977  set location, else fails)
11978  Fns -> R only
11979  F-nshs -> Nothing (no repeats permitted)
11980  Fjo -> R only
11981  Frh -> R only
11982  Fer -> R only
11983  Frh-sh -> R only
11984  Fns-sh -> R only
11985  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
11986  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11987  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11988  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11989  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11990  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11991  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11992  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11993  Repeat -> Nothing
11994 
11995  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
11996  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
11997  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
11998  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
11999  Check all times increase or stay same through ActionVector
12000  Cycle through all entries in vector setting arr & dep times based on above list
12001  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
12002  Check locations match the arr & dep TimeLoc entries
12003  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
12004  Make above valid succession checks
12005  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
12006  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
12007  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
12008  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
12009  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
12010  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
12011  Set train info for Sfs & Sns entries
12012  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
12013  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
12014  element at each end, or length of 3 & 1 extra element at either end
12015  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
12016  Check all Cmds have EventTime set & Arr & Dep times = -1
12017  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
12018  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
12019 
12020  Give messages in function if errors detected and clear the vector. Return false for failure.
12021 */
12022 
12023 /* Earlier checks:-
12024  Checks carried out with error messages in this function:-
12025  At least one comma in the line (it's based on a csv file);
12026  No entries following train information;
12027  At least one comma in remainder after train information (i.e at least a start and a finish entry);
12028  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
12029  First entry not a start entry;
12030  Train information incomplete before a start entry;
12031  Entry follows a finish entry but doesn't begin with 'R';
12032  SplitEntry returns false in a finish entry - message repeats the entry for information;
12033  Last action entry isn't a finish entry.
12034 
12035  Function returns false with no message if:-
12036  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
12037  time is found at all then an error message is given in the calling function);
12038  SplitTrainInfo returns false (message given in called function);
12039  SplitRepeat returns false (message given in called function).
12040 
12041 Double crosslink (shuttle) table:
12042 
12043 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
12044  Code ShuttleLink- EntryPtr ShuttleLink-
12045  HeadCode EntryPtr
12046 
12047 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
12048 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
12049 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
12050 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
12051 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
12052 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
12053 
12054 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
12055 
12056 */{
12057  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
12058  if(TrainDataVector.empty())
12059  {
12060  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
12061  TrainDataVector.clear();
12062  Utilities->CallLogPop(1832);
12063  return(false);
12064  }
12065 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
12066  1) must have at least one actionvector entry
12067  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
12068  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
12069  4) first entry must be a start;
12070  4a) if first entry is Snt and not signallercontrol and second is a finish then it must be at a location with zero start speed
12071  5) a start must be the first entry;
12072  6) a repeat entry must be the last;
12073  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
12074  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
12075  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
12076 */
12077 
12078  TwoLocationList.clear(); //empty the list to begin with, added at v2.9.1
12079  TwoLocationFlag = false; //added at v2.9.1
12080  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
12081  {
12082  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12083  if(TrainDataVector.at(x).ActionVector.empty())
12084  {
12085  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
12086  TrainDataVector.clear();
12087  Utilities->CallLogPop(1833);
12088  return(false);
12089  }
12090  }
12091  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
12092  {
12093  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12094  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12095  if(!(AVEntry0.SignallerControl))
12096  {
12097  if(TrainDataVector.at(x).ActionVector.size() == 1)
12098  {
12099  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
12100  TrainDataVector.clear();
12101  Utilities->CallLogPop(1822);
12102  return(false);
12103  }
12104  }
12105  }
12106  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
12107  {
12108  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12109  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12110  if(AVEntry0.SignallerControl)
12111  {
12112  if(TrainDataVector.at(x).ActionVector.size() > 2)
12113  {
12114  SecondPassMessage(GiveMessages,
12115  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
12116  TDEntry.HeadCode);
12117  TrainDataVector.clear();
12118  Utilities->CallLogPop(1837);
12119  return(false);
12120  }
12121  if(TrainDataVector.at(x).ActionVector.size() > 1)
12122  {
12123  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12124  if(AVEntry1.FormatType != Repeat)
12125  {
12126  SecondPassMessage(GiveMessages,
12127  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
12128  TrainDataVector.clear();
12129  Utilities->CallLogPop(1838);
12130  return(false);
12131  }
12132  }
12133  }
12134  }
12135  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
12136  {
12137  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12138  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12139  if(AVEntry0.SequenceType != Start)
12140  {
12141  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
12142  TrainDataVector.clear();
12143  Utilities->CallLogPop(1824);
12144  return(false);
12145  }
12146  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
12147  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
12148  // and others for a located Snt, but those checks done later
12149  {
12150  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12151  // must be a second entry if first not signallercontrol
12152  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
12153  {
12154  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
12155  TDEntry.HeadCode);
12156  TrainDataVector.clear();
12157  Utilities->CallLogPop(2046);
12158  return(false);
12159  }
12160  }
12161  }
12162  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
12163  {
12164  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12165  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12166  {
12167  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12168  if((AVEntry.SequenceType == Start) && (y != 0))
12169  {
12170  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
12171  TrainDataVector.clear();
12172  Utilities->CallLogPop(1825);
12173  return(false);
12174  }
12175  }
12176  }
12177  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
12178  {
12179  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12180  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12181  {
12182  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12183  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
12184  {
12185  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
12186  TrainDataVector.clear();
12187  Utilities->CallLogPop(1826);
12188  return(false);
12189  }
12190  }
12191  }
12192  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
12193  {
12194  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12195  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12196  {
12197  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12198  if((y == 0) && AVEntry.SignallerControl)
12199  {
12200  break;
12201  }
12202  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
12203  {
12204  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
12205  {
12206  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
12207  TrainDataVector.clear();
12208  Utilities->CallLogPop(1827);
12209  return(false);
12210  }
12211  if(AVEntry.FormatType == Repeat)
12212  {
12213  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
12214  if(LastAVEntry.SequenceType != Finish)
12215  {
12216  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
12217  TrainDataVector.clear();
12218  Utilities->CallLogPop(1828);
12219  return(false);
12220  }
12221  }
12222  }
12223  }
12224  }
12225  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
12226  {
12227  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12228  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12229  {
12230  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12231  if(AVEntry.SequenceType == Finish)
12232  {
12233  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
12234  {
12235  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
12236  TrainDataVector.clear();
12237  Utilities->CallLogPop(1829);
12238  return(false);
12239  }
12240  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
12241  {
12242  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12243  {
12244  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
12245  TDEntry.HeadCode);
12246  TrainDataVector.clear();
12247  Utilities->CallLogPop(1830);
12248  return(false);
12249  }
12250  }
12251  }
12252  }
12253  }
12254 
12255  // end of new preliminary checks
12256 
12257  // check ActionVector present and check start event successor validity
12258  // For Snt & Snt-sh set location if stopped, don't set any times yet
12259  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12260  {
12261  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12262  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12263  // use reference so can change internals where necessary
12264  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
12265  {
12266  AnsiString LocationName = "";
12267  if(IsSNTEntryLocated(0, TDEntry, LocationName))
12268  // it is at a location
12269  {
12270  if(TDEntry.StartSpeed == 0) // stopped
12271  {
12272  AVEntry0.LocationName = LocationName;
12273  AVEntry0.LocationType = AtLocation;
12274  // check successor validity for located Snt that isn't a SignallerControl entry
12275  if(!AVEntry0.SignallerControl)
12276  {
12277  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12278  // at least 2 entries present checked in integrity check so (1) valid
12279  if(!AtLocSuccessor(AVEntry1))
12280  {
12281  // Frh following Snt-sh will return false in location check, so no need to check here
12282  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
12283  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12284  TrainDataVector.clear();
12285  Utilities->CallLogPop(523);
12286  return(false);
12287  }
12288  }
12289  }
12290  else
12291  {
12292  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
12293  TDEntry.HeadCode);
12294  TrainDataVector.clear();
12295  Utilities->CallLogPop(791);
12296  return(false);
12297  }
12298  }
12299  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
12300  {
12301  if(AVEntry0.Command == "Snt-sh")
12302  {
12303  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
12304  TrainDataVector.clear();
12305  Utilities->CallLogPop(1042);
12306  return(false);
12307  }
12308  AVEntry0.LocationType = EnRoute;
12309  if(!AVEntry0.SignallerControl)
12310  {
12311  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12312  // at least 2 entries checked in integrity check so (1) valid
12313  if(!MovingSuccessor(AVEntry1))
12314  {
12315  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
12316  TDEntry.HeadCode);
12317  TrainDataVector.clear();
12318  Utilities->CallLogPop(790);
12319  return(false);
12320  }
12321  }
12322  }
12323  }
12324  // check other start successors
12325  else if(AVEntry0.SequenceType == Start)
12326  {
12327  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12328  // at least 2 entries present checked in integrity check so (1) valid
12329  if(!AtLocSuccessor(AVEntry1))
12330  {
12331  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
12332  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12333  TrainDataVector.clear();
12334  Utilities->CallLogPop(793);
12335  return(false);
12336  }
12337  }
12338  }
12339 
12340  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
12341  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12342  {
12343  bool FoundFlag = false;
12344  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12345  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
12346  // use reference so can change internals
12347  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
12348  {
12349  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
12350  {
12351  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
12352  if(AVEntry2.FormatType == TimeLoc)
12353  {
12354  FoundFlag = true;
12355  AVEntry.LocationName = AVEntry2.LocationName;
12356  break;
12357  }
12358  }
12359  if(!FoundFlag)
12360  {
12361  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
12362  TDEntry.HeadCode);
12363  TrainDataVector.clear();
12364  Utilities->CallLogPop(851);
12365  return(false);
12366  }
12367  }
12368  }
12369 
12370  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
12371  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12372  {
12373  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12374  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12375  {
12376  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12377  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
12378  {
12379  if(AVEntry.LocationName == "")
12380  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
12381  {
12382  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
12383  ": an event should have had a location name associated with it but it could not be found");
12384  TrainDataVector.clear();
12385  Utilities->CallLogPop(1831);
12386  return(false);
12387  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
12388  }
12389  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
12390  {
12391  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
12392  // use reference so can change internals where necessary
12393  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
12394  {
12395  AVEntry2.LocationName = AVEntry.LocationName;
12396  }
12397  else
12398  {
12399  break;
12400  }
12401  }
12402  }
12403  }
12404  }
12405  // all location names now set
12406 
12407  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
12408  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12409  {
12410  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12411  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12412  {
12413  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12414  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
12415  {
12416  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
12417  // i.e at least one more, must be a repeat
12418  {
12419  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12420  {
12421  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
12422  TrainDataVector.clear();
12423  Utilities->CallLogPop(798);
12424  return(false);
12425  }
12426  }
12427  }
12428  if(AVEntry.Command == "F-nshs")
12429  {
12430  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12431  // i.e has to be the last
12432  {
12433  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
12434  TrainDataVector.clear();
12435  Utilities->CallLogPop(1049);
12436  return(false);
12437  }
12438  }
12439  if(AVEntry.Command == "pas")
12440  {
12441  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12442  {
12443  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
12444  TrainDataVector.clear();
12445  Utilities->CallLogPop(1518);
12446  return(false);
12447  }
12448  }
12449  if(AVEntry.Command == "jbo")
12450  {
12451  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12452  {
12453  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
12454  TrainDataVector.clear();
12455  Utilities->CallLogPop(800);
12456  return(false);
12457  }
12458  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12459  if(!AtLocSuccessor(AVEntry2))
12460  {
12461  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
12462  ". The event isn't valid for a stationary train.");
12463  TrainDataVector.clear();
12464  Utilities->CallLogPop(801);
12465  return(false);
12466  }
12467  }
12468  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
12469  {
12470  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12471  {
12472  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
12473  TrainDataVector.clear();
12474  Utilities->CallLogPop(802);
12475  return(false);
12476  }
12477  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12478  if(!AtLocSuccessor(AVEntry2))
12479  {
12480  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
12481  ". The event isn't valid for a stationary train.");
12482  TrainDataVector.clear();
12483  Utilities->CallLogPop(803);
12484  return(false);
12485  }
12486  }
12487  if(AVEntry.Command == "cdt")
12488  {
12489  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12490  {
12491  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
12492  TrainDataVector.clear();
12493  Utilities->CallLogPop(804);
12494  return(false);
12495  }
12496  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12497  if(!AtLocSuccessor(AVEntry2))
12498  {
12499  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
12500  ". The event isn't valid for a stationary train.");
12501  TrainDataVector.clear();
12502  Utilities->CallLogPop(805);
12503  return(false);
12504  }
12505  }
12506  if(AVEntry.FormatType == TimeTimeLoc)
12507  {
12508  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12509  {
12510  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
12511  TrainDataVector.clear();
12512  Utilities->CallLogPop(806);
12513  return(false);
12514  }
12515  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12516  if(!MovingSuccessor(AVEntry2))
12517  {
12518  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
12519  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
12520  TrainDataVector.clear();
12521  Utilities->CallLogPop(807);
12522  return(false);
12523  }
12524  }
12525  if(AVEntry.FormatType == PassTime)
12526  {
12527  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12528  {
12529  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
12530  TrainDataVector.clear();
12531  Utilities->CallLogPop(1530);
12532  return(false);
12533  }
12534  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12535  if(!MovingSuccessor(AVEntry2))
12536  {
12537  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
12538  ". The event isn't valid for a moving train.");
12539  TrainDataVector.clear();
12540  Utilities->CallLogPop(1531);
12541  return(false);
12542  }
12543  }
12544  if(AVEntry.FormatType == Repeat)
12545  {
12546  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12547  {
12548  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
12549  TrainDataVector.clear();
12550  Utilities->CallLogPop(808);
12551  return(false);
12552  }
12553  }
12554  }
12555  }
12556 
12557  // set arrival & departure times for TimeLocs & set their EventTimes to -1
12558  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12559  {
12560  bool LastEntryIsAnArrival = false;
12561  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12562  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
12563  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12564  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12565  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
12566  {
12567  LastEntryIsAnArrival = false;
12568  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12569  {
12570  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12571  if(AVEntry.FormatType == TimeLoc)
12572  {
12573  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12574  {
12575  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12576  }
12577  if(LastEntryIsAnArrival)
12578  {
12579  AVEntry.DepartureTime = AVEntry.EventTime;
12580  AVEntry.EventTime = TDateTime(-1);
12581  LastEntryIsAnArrival = false;
12582  }
12583  else // last entry a departure
12584  {
12585  AVEntry.ArrivalTime = AVEntry.EventTime;
12586  AVEntry.EventTime = TDateTime(-1);
12587  LastEntryIsAnArrival = true;
12588  }
12589  }
12590  }
12591  }
12592  else // all others stopped at beginning
12593  {
12594  LastEntryIsAnArrival = true;
12595  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12596  {
12597  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12598  if(AVEntry.FormatType == TimeLoc)
12599  {
12600  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12601  {
12602  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12603  }
12604  if(LastEntryIsAnArrival)
12605  {
12606  AVEntry.DepartureTime = AVEntry.EventTime;
12607  AVEntry.EventTime = TDateTime(-1);
12608  LastEntryIsAnArrival = false;
12609  }
12610  else // last entry a departure
12611  {
12612  AVEntry.ArrivalTime = AVEntry.EventTime;
12613  AVEntry.EventTime = TDateTime(-1);
12614  LastEntryIsAnArrival = true;
12615  }
12616  }
12617  }
12618  }
12619  }
12620  // perform remaining successor checks for TimeLocs
12621  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12622  {
12623  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12624  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12625  {
12626  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12627  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
12628  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
12629  {
12630  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12631  {
12632  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
12633  TrainDataVector.clear();
12634  Utilities->CallLogPop(809);
12635  return(false);
12636  }
12637  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12638  if(!AtLocSuccessor(AVEntry2))
12639  {
12640  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
12641  ". The event isn't valid for a stationary train.");
12642  TrainDataVector.clear();
12643  Utilities->CallLogPop(810);
12644  return(false);
12645  }
12646  }
12647  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
12648  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
12649  {
12650  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12651  {
12652  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
12653  TrainDataVector.clear();
12654  Utilities->CallLogPop(811);
12655  return(false);
12656  }
12657  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12658  if(!MovingSuccessor(AVEntry2))
12659  {
12660  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
12661  ". The event isn't valid for a moving train.");
12662  TrainDataVector.clear();
12663  Utilities->CallLogPop(812);
12664  return(false);
12665  }
12666  }
12667  }
12668  }
12669 
12670  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
12671  // & repeats have no times set
12672  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12673  {
12674  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12675  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12676  {
12677  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12678  if(AVEntry.FormatType == TimeLoc)
12679  {
12680  if(AVEntry.EventTime != TDateTime(-1))
12681  {
12682  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12683  }
12684  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
12685  {
12686  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
12687  }
12688  }
12689  if(AVEntry.FormatType == TimeTimeLoc)
12690  {
12691  if(AVEntry.EventTime != TDateTime(-1))
12692  {
12693  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12694  }
12695  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
12696  {
12697  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
12698  }
12699  }
12700  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
12701  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
12702  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
12703  {
12704  if(AVEntry.EventTime == TDateTime(-1))
12705  {
12706  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
12707  }
12708  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12709  {
12710  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
12711  }
12712  }
12713  if(AVEntry.FormatType == Repeat)
12714  {
12715  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12716  {
12717  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
12718  }
12719  }
12720  }
12721  }
12722 
12723  // check times stay same or increase, note that can have time of 0 if include midnight
12724  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12725  {
12726  TDateTime CurrentTime = TTClockTime; // the timetable start time
12727  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12728  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12729  {
12730  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12731  if(AVEntry.FormatType == Repeat)
12732  {
12733  break;
12734  }
12735  if(AVEntry.FormatType == FinRemHere)
12736  {
12737  break;
12738  }
12739  if(AVEntry.FormatType == TimeTimeLoc)
12740  {
12741  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
12742  {
12743  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
12744  TDEntry.HeadCode);
12745  TrainDataVector.clear();
12746  Utilities->CallLogPop(813);
12747  return(false);
12748  }
12749  if(AVEntry.ArrivalTime < CurrentTime)
12750  {
12751  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
12752  TDEntry.HeadCode);
12753  TrainDataVector.clear();
12754  Utilities->CallLogPop(814);
12755  return(false);
12756  }
12757  CurrentTime = AVEntry.DepartureTime;
12758  continue;
12759  }
12760  if(AVEntry.FormatType == TimeLoc)
12761  {
12762  if(AVEntry.ArrivalTime >= TDateTime(0))
12763  {
12764  if(AVEntry.ArrivalTime < CurrentTime)
12765  {
12766  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12767  TrainDataVector.clear();
12768  Utilities->CallLogPop(815);
12769  return(false);
12770  }
12771  CurrentTime = AVEntry.ArrivalTime;
12772  }
12773  else
12774  {
12775  if(AVEntry.DepartureTime < CurrentTime)
12776  // both may be 0 legitimately so must allow for this
12777  {
12778  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12779  TrainDataVector.clear();
12780  Utilities->CallLogPop(816);
12781  return(false);
12782  }
12783  CurrentTime = AVEntry.DepartureTime;
12784  }
12785  continue;
12786  }
12787  if(AVEntry.EventTime < CurrentTime)
12788  // all others have EventTime set
12789  {
12790  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
12791  ", may be before timetable start time");
12792  TrainDataVector.clear();
12793  Utilities->CallLogPop(835);
12794  return(false);
12795  }
12796  CurrentTime = AVEntry.EventTime;
12797  continue;
12798  }
12799  }
12800 
12801  // check locations consistent
12802  AnsiString LastLocationName = "";
12803 
12804  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12805  {
12806  bool LastEntryIsAnArrival = false;
12807  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12808  // first deal with moving Snt entries (all else stopped)
12809  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
12810  {
12811  LastEntryIsAnArrival = false;
12812  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
12813  if(LastLocationName != "")
12814  {
12815  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
12816  }
12817  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
12818  y++) // note that immediate successor to a moving Snt can only be a Moving type
12819  {
12820  // if it's a SignallerControl entry then the condition isn't met
12821  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12822  if(AVEntry.FormatType == Repeat)
12823  {
12824  break; // repeat = reached end (+allows repeat after signaller controlled entry)
12825  }
12826  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12827  {
12828  if(AVEntry.LocationName != LastLocationName)
12829  {
12830  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12831  AVEntry.Command);
12832  TrainDataVector.clear();
12833  Utilities->CallLogPop(823);
12834  return(false);
12835  }
12836  }
12837  else if(AVEntry.FormatType == TimeCmd)
12838  // cdt is the only TimeCmd
12839  {
12840  if(AVEntry.LocationName != LastLocationName)
12841  {
12842  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12843  AVEntry.Command);
12844  TrainDataVector.clear();
12845  Utilities->CallLogPop(824);
12846  return(false);
12847  }
12848  }
12849  else if(AVEntry.FormatType == TimeTimeLoc)
12850  {
12851  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12852  // last entry must be a departure or would have failed earlier
12853  {
12854  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12855  TwoLocationFlag = true;
12856 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12857 // TwoOrMoreLocationsWarningGiven = true;
12858  }
12859  LastLocationName = AVEntry.LocationName;
12860  LastEntryIsAnArrival = false;
12861  }
12862  else if(AVEntry.FormatType == TimeLoc)
12863  {
12864  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12865  {
12866  SecondPassMessage(GiveMessages,
12867  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12868  TrainDataVector.clear();
12869  Utilities->CallLogPop(826);
12870  return(false);
12871  }
12872  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
12873  {
12874  SecondPassMessage(GiveMessages,
12875  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
12876  TrainDataVector.clear();
12877  Utilities->CallLogPop(827);
12878  return(false);
12879  }
12880  LastLocationName = AVEntry.LocationName;
12881  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12882  }
12883  }
12884  }
12885  else // all stationary starting entries
12886  {
12887  LastEntryIsAnArrival = true;
12888  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
12889  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12890  {
12891  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12892  if(AVEntry.FormatType == Repeat)
12893  {
12894  break;
12895  }
12896  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12897  // no need to add anything for shuttle starts since they are at loc (0) anyway
12898  {
12899  if(AVEntry.LocationName != LastLocationName)
12900  {
12901  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12902  AVEntry.Command);
12903  TrainDataVector.clear();
12904  Utilities->CallLogPop(828);
12905  return(false);
12906  }
12907  }
12908  else if(AVEntry.FormatType == TimeCmd)
12909  // cdt is the only TimeCmd
12910  {
12911  if(AVEntry.LocationName != LastLocationName)
12912  {
12913  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12914  AVEntry.Command);
12915  TrainDataVector.clear();
12916  Utilities->CallLogPop(829);
12917  return(false);
12918  }
12919  }
12920  else if(AVEntry.FormatType == TimeTimeLoc)
12921  {
12922  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12923  // last entry must be a departure or would have failed earlier
12924  {
12925  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12926  TwoLocationFlag = true;
12927 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12928 // TwoOrMoreLocationsWarningGiven = true;
12929  }
12930  LastLocationName = AVEntry.LocationName;
12931  LastEntryIsAnArrival = false;
12932  }
12933  else if(AVEntry.FormatType == TimeLoc)
12934  {
12935  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12936  {
12937  SecondPassMessage(GiveMessages,
12938  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12939  TrainDataVector.clear();
12940  Utilities->CallLogPop(831);
12941  return(false);
12942  }
12943  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
12944  {
12945  SecondPassMessage(GiveMessages,
12946  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
12948 // TrainDataVector.clear();
12949 // Utilities->CallLogPop(832);
12950 // return false;
12951  }
12952  LastLocationName = AVEntry.LocationName;
12953  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12954  }
12955  }
12956  }
12957  }
12958 
12959  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
12960  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
12961  AnsiString LocationNameToBeChecked = "";
12962 
12963  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12964  {
12965  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12966  unsigned int y = 0;
12967  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
12968  // first discard unlocated Snt entries as they don't have location name set
12969  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12970  {
12971  y = 1;
12972  }
12973  while(y < TDEntry.ActionVector.size())
12974  // need to check each location name separately in turn, skipped for SignallerControl entries
12975  {
12976  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
12977  {
12978  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12979  }
12980  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
12981  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
12982  {
12983  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
12984  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
12985  {
12986  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12987  }
12988  if(AVEntry.Command == "cdt")
12989  {
12990  break; // out of the 'z' loop since the check is only valid up to a change of direction
12991  }
12992  if(AVEntry.LocationName == LocationNameToBeChecked)
12993  {
12994  continue; // keep going while name same
12995  }
12996  if(AVEntry.LocationName != LocationNameToBeChecked)
12997  // if name different check forwards to see if repeats
12998  {
12999  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
13000  {
13001  if(TDEntry.ActionVector.at(a).Command == "cdt")
13002  {
13003  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
13004  }
13005  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
13006  {
13007  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
13008  TwoLocationFlag = true;
13009 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
13010 // TwoOrMoreLocationsWarningGiven = true;
13011  }
13012  }
13013  break; // out of the 'z' loop since have checked 'a' as far as need to
13014  }
13015  }
13016  y++;
13017  }
13018  }
13019  if(TwoLocationFlag)
13020  {
13021  TwoLocationList.sort(); //need to sort first in alphabetical order to ensure all duplictes removed
13022  TwoLocationList.unique(); //remove duplicates
13023  }
13024 
13025  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
13026  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13027  {
13028  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13029  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13030  {
13031  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13032  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
13033  {
13034  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
13035  }
13036  AnsiString LocName = "";
13037  // dummy, only used so can call IsSNTEntryLocated
13038  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
13039  {
13040  if(AVEntry.LocationName == "")
13041  {
13042  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
13043  }
13044  }
13045  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
13046  {
13047  if(AVEntry.LocationName != "")
13048  {
13049  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
13050  }
13051  }
13052  }
13053  }
13054 
13055 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
13056  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
13057  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
13058 
13059  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
13060  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
13061 
13062  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
13063  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
13064  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
13065 */
13066  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
13067  {
13068  // non-shuttles & non-repeating links separately, but don't check that there isn't a
13069  // duplicate between a non-repeating shuttle and another - leave original tests in as
13070  // these also set the pointers
13071  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13072  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13073  {
13074  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13075  if(AVEntry.OtherHeadCode != "")
13076  {
13077  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
13078  {
13079  Utilities->CallLogPop(1584);
13080  return(false); // error message given in called function
13081  }
13082  }
13083  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13084  {
13085  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
13086  {
13087  Utilities->CallLogPop(1585);
13088  return(false); // error message given in called function
13089  }
13090  }
13091  }
13092  }
13093 
13094  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13095  {
13096  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13097  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13098  {
13099  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13100  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13101  {
13102  if(AVEntry.OtherHeadCode != "")
13103  {
13104  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
13105  // false = non-shuttle
13106  {
13107  Utilities->CallLogPop(864);
13108  return(false); // error message given in called function
13109  }
13110  }
13111  }
13112  }
13113  }
13114 
13115  // now repeat the check just for the shuttles
13116  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13117  {
13118  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13119  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13120  {
13121  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13122  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
13123  {
13124  if(AVEntry.OtherHeadCode != "")
13125  {
13126  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
13127  // true = shuttle
13128  {
13129  Utilities->CallLogPop(1100);
13130  return(false); // error message given in called function
13131  }
13132  }
13133  }
13134  }
13135  }
13136 
13137  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
13138  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13139  {
13140  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13141  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13142  {
13143  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13144  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13145  {
13147  {
13148  Utilities->CallLogPop(1060);
13149  return(false); // error message given in called function
13150  }
13151  }
13152  }
13153  }
13154 
13155  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
13156  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
13157  // don't ever need to and as designed would skip repeats
13158  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13159  {
13160  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13161  {
13162  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13163  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
13164  {
13165  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
13166  {
13167  Utilities->CallLogPop(1090);
13168  return(false); // error message given in called function
13169  }
13170  }
13171  }
13172  }
13173 
13174  // check all entries have all types set to something
13175  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13176  {
13177  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13178  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13179  {
13180  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13181  if(AVEntry.FormatType == NoFormat)
13182  {
13183  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
13184  }
13185  else if(AVEntry.SequenceType == NoSequence)
13186  {
13187  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
13188  }
13189  else if(AVEntry.LocationType == NoLocation)
13190  {
13191  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
13192  }
13193  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
13194  {
13195  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
13196  }
13197  }
13198  }
13199 
13200  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
13201  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13202  {
13203  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
13204  // non-const reference so can alter content
13205  TTrainOperatingData TData;
13206  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
13207  if(LastAVEntry.FormatType == Repeat) // check if a repeat
13208  {
13209 /*
13210  class TTrainOperatingData
13211  {
13212  public:
13213  int TrainID; - default, set at construction
13214  TActionEventType EventReported; used during operation
13215  TRunningEntry RunningEntry; - default, set at construction
13216  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
13217  };
13218 */
13219  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
13220  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
13221  {
13222  TDEntry.TrainOperatingDataVector.push_back(TData);
13223  }
13224  }
13225  else
13226  {
13227  TDEntry.NumberOfTrains = 1;
13228  }
13229  }
13230 
13231  // check that don't include any Continuation names
13232  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13233  {
13234  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13235  {
13236  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
13237  AnsiString HC = TrainDataVector.at(x).HeadCode;
13238  if(LocName != "")
13239  {
13240  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
13241  {
13242  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
13243  TrainDataVector.clear();
13244  Utilities->CallLogPop(1578);
13245  return(false);
13246  }
13247  }
13248  }
13249  }
13250 
13251  // check that all repeat times below 96h
13252  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13253  {
13254  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
13255  int IncMinutes = 0;
13256  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
13257  {
13258  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
13259  }
13260  else
13261  {
13262  continue; // basic times already checked in CheckTimeValidity
13263  }
13264  AnsiString HC = TrainDataVector.at(x).HeadCode;
13265  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13266  {
13267  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
13268  {
13269  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13270  {
13271  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
13272  TrainDataVector.clear();
13273  Utilities->CallLogPop(1818);
13274  return(false);
13275  }
13276  }
13277  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
13278  {
13279  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13280  // 3d 23h 59m = 3.9993055556
13281  {
13282  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13283  TrainDataVector.clear();
13284  Utilities->CallLogPop(1819);
13285  return(false);
13286  }
13287  }
13288  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
13289  {
13290  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13291  // 3d 23h 59m = 3.9993055556
13292  {
13293  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13294  TrainDataVector.clear();
13295  Utilities->CallLogPop(1820);
13296  return(false);
13297  }
13298  }
13299  }
13300  }
13301 
13302  // Now that all set up change any extended headcodes back to ordinary headcodes
13303  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13304  {
13305  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
13306  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13307  {
13308  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
13309  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
13310  }
13311  }
13312 
13313  // SaveTrainDataVectorToFile(0);//test
13315  Utilities->CallLogPop(782);
13316  return(true);
13317 }
13318 
13319 // ---------------------------------------------------------------------------
13320 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
13322 {
13323  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
13324 }
13325 
13326 // ---------------------------------------------------------------------------
13327 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
13329 {
13330  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
13331  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
13332  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
13333 }
13334 
13335 // ---------------------------------------------------------------------------
13336 
13337 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
13338 {
13339  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
13340  if(HeadCode.Length() > 4) // ignore otherwise
13341  {
13342  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
13343  }
13344  Utilities->CallLogPop(1593);
13345 }
13346 
13347 // ---------------------------------------------------------------------------
13348 
13349 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
13350 {
13351  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
13352  SecondHeadCode);
13353  int ForwardCount = 0;
13354  int ReverseCount = 0;
13355 
13356  if(MainHeadCode == SecondHeadCode)
13357  {
13358  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
13359  TrainDataVector.clear();
13360  Utilities->CallLogPop(1594);
13361  return(false);
13362  }
13363  // forward check
13364  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13365  {
13366  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13367  if(TDEntry.HeadCode == MainHeadCode)
13368  {
13369  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13370  {
13371  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13372  if(AVEntry.OtherHeadCode == SecondHeadCode)
13373  {
13374  ForwardCount++;
13375  }
13376  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
13377  // need own check in case both 'Other' & 'NonRepeating' have same headcode
13378  {
13379  ForwardCount++;
13380  }
13381  }
13382  }
13383  }
13384  if(ForwardCount == 0)
13385  // this is an exception because the headcodes are selected in the same order as the forward check
13386  {
13387  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
13388  }
13389  if(ForwardCount > 2)
13390  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
13391  {
13392  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
13393  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13394  TrainDataVector.clear();
13395  Utilities->CallLogPop(1587);
13396  return(false);
13397  }
13398  // reverse check
13399  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13400  {
13401  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13402  if(TDEntry.HeadCode == SecondHeadCode)
13403  {
13404  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13405  {
13406  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13407  if(AVEntry.OtherHeadCode == MainHeadCode)
13408  {
13409  ReverseCount++;
13410  }
13411  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
13412  {
13413  ReverseCount++;
13414  }
13415  }
13416  }
13417  }
13418 
13419  if(ReverseCount == 0)
13420  {
13421  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
13422  TrainDataVector.clear();
13423  Utilities->CallLogPop(1588);
13424  return(false);
13425  }
13426  if(ReverseCount > 2)
13427  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
13428  {
13429  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
13430  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13431  TrainDataVector.clear();
13432  Utilities->CallLogPop(1589);
13433  return(false);
13434  }
13435  if(ForwardCount != ReverseCount)
13436  {
13437  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
13438  " than the other way round");
13439  TrainDataVector.clear();
13440  Utilities->CallLogPop(1610);
13441  return(false);
13442  }
13443  Utilities->CallLogPop(1590);
13444  return(true);
13445 }
13446 
13447 // ---------------------------------------------------------------------------
13448 
13449 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
13450 /* Return false for no find or more than one find, check correct types of link
13451  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
13452  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
13453  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
13454  Then do the same in reverse.
13455  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
13456  if main is Fns other must be Sns; if main is jbo other must be Fjo.
13457  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
13458  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
13459  for Sfs & Sns services. Finally check the repeat entries if present are consistent
13460 
13461  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
13462  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
13463 
13464  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
13465 */
13466 
13467 {
13468  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
13469  int ForwardCount = 0;
13470  int ReverseCount = 0;
13471  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
13472  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
13473  TTrainDataEntry *MainTrainDataPtr = 0;
13474  TTrainDataEntry *OtherTrainDataPtr = 0;
13475 
13476  // forward check
13477  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13478  {
13479  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13480  if(TDEntry.HeadCode == MainHeadCode)
13481  {
13482  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13483  {
13484  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13485  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13486  {
13487  if(AVEntry.OtherHeadCode == OtherHeadCode)
13488  {
13489  MainTrainDataPtr = &TrainDataVector.at(x);
13490  ForwardEntryPtr = &AVEntry;
13491  ForwardCount++;
13492  ForwardTDVectorNumber = x;
13493  }
13494  }
13495  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
13496  (AVEntry.Command == "Frh-sh")))
13497  {
13498  if(AVEntry.OtherHeadCode == OtherHeadCode)
13499  {
13500  MainTrainDataPtr = &TrainDataVector.at(x);
13501  ForwardEntryPtr = &AVEntry;
13502  ForwardCount++;
13503  ForwardTDVectorNumber = x;
13504  }
13505  }
13506  }
13507  }
13508  }
13509  if(ForwardCount == 0)
13510  // this is an exception because the headcodes are selected in the same order as the forward check
13511  {
13512  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
13513  }
13514  if(ForwardCount > 1)
13515  {
13516  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
13517  MainHeadCode);
13518  TrainDataVector.clear();
13519  Utilities->CallLogPop(836);
13520  return(false);
13521  }
13522  // reverse check
13523  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13524  {
13525  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13526  if(TDEntry.HeadCode == OtherHeadCode)
13527  {
13528  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13529  {
13530  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13531  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13532  {
13533  if(AVEntry.OtherHeadCode == MainHeadCode)
13534  {
13535  OtherTrainDataPtr = &TrainDataVector.at(x);
13536  ReverseCount++;
13537  ReverseEntryPtr = &AVEntry;
13538  ReverseTDVectorNumber = x;
13539  }
13540  }
13541  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
13542  {
13543  if(AVEntry.OtherHeadCode == MainHeadCode)
13544  {
13545  OtherTrainDataPtr = &TrainDataVector.at(x);
13546  ReverseCount++;
13547  ReverseEntryPtr = &AVEntry;
13548  ReverseTDVectorNumber = x;
13549  }
13550  }
13551  }
13552  }
13553  }
13554 
13555  if(ReverseCount == 0)
13556  {
13557  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
13558  TrainDataVector.clear();
13559  Utilities->CallLogPop(837);
13560  return(false);
13561  }
13562  if(ReverseCount > 1)
13563  {
13564  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
13565  OtherHeadCode);
13566  TrainDataVector.clear();
13567  Utilities->CallLogPop(838);
13568  return(false);
13569  }
13570  // these will all be false for !Shuttle
13571  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
13572  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
13573  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
13574  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
13575 
13576  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
13577  {
13578  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
13579  TrainDataVector.clear();
13580  Utilities->CallLogPop(1058);
13581  return(false);
13582  }
13583  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
13584  {
13585  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
13586  TrainDataVector.clear();
13587  Utilities->CallLogPop(1059);
13588  return(false);
13589  }
13590  if(ForwardEntryPtr->LocationName == "")
13591  {
13592  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13593  ". One or other service does not have a location set");
13594  TrainDataVector.clear();
13595  Utilities->CallLogPop(526);
13596  return(false);
13597  }
13598  if(ReverseEntryPtr->LocationName == "")
13599  {
13600  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13601  ". One or other service does not have a location set");
13602  TrainDataVector.clear();
13603  Utilities->CallLogPop(527);
13604  return(false);
13605  }
13606  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
13607  {
13608  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13609  " is at a different location to the referencing train " + MainHeadCode);
13610  TrainDataVector.clear();
13611  Utilities->CallLogPop(842);
13612  return(false);
13613  }
13614  // ignore shuttle repeat links for first time check
13615  if(!Shuttle)
13616  {
13617  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
13618  {
13619  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13620  " has a different event time to the referencing train " + MainHeadCode);
13621  TrainDataVector.clear();
13622  Utilities->CallLogPop(525);
13623  return(false);
13624  }
13625  }
13626  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
13627  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
13628  if(ForwardShuttleStart && ReverseShuttleFinish)
13629  // Shuttle must be true if these are true
13630  {
13631  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
13632  {
13633  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
13634  " first repeat restart time not consistent with finish service " + OtherHeadCode);
13635  TrainDataVector.clear();
13636  Utilities->CallLogPop(1055);
13637  return(false);
13638  }
13639  }
13640  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
13641  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
13642  {
13643  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13644  {
13645  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
13646  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13647  TrainDataVector.clear();
13648  Utilities->CallLogPop(528);
13649  return(false);
13650  }
13651  }
13652  if(ReverseEntryPtr->Command == "Fjo")
13653  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
13654  {
13655  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13656  {
13657  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
13658  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13659  TrainDataVector.clear();
13660  Utilities->CallLogPop(862);
13661  return(false);
13662  }
13663  }
13664  if(ReverseEntryPtr->Command == "Fns")
13665  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
13666  {
13667  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13668  {
13669  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
13670  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13671  TrainDataVector.clear();
13672  Utilities->CallLogPop(529);
13673  return(false);
13674  }
13675  }
13676  if(ForwardEntryPtr->Command == "Sfs")
13677  {
13678  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
13679  {
13680  SecondPassMessage(GiveMessages,
13681  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
13682  MainHeadCode);
13683  TrainDataVector.clear();
13684  Utilities->CallLogPop(530);
13685  return(false);
13686  }
13687  }
13688  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
13689  {
13690  if(ReverseEntryPtr->Command != "Sfs")
13691  {
13692  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
13693  MainHeadCode);
13694  TrainDataVector.clear();
13695  Utilities->CallLogPop(839);
13696  return(false);
13697  }
13698  else
13699  {
13700  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
13701  {
13702  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
13703  TrainDataVector.clear();
13704  Utilities->CallLogPop(849);
13705  return(false);
13706  }
13707  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
13708  {
13709  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
13710  TrainDataVector.clear();
13711  Utilities->CallLogPop(850);
13712  return(false);
13713  }
13714  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
13715  {
13716  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
13717  TrainDataVector.clear();
13718  Utilities->CallLogPop(846);
13719  return(false);
13720  }
13721  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13722  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13723  if(OtherTrainDataPtr->Description == "")
13724  {
13725  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13726  }
13727  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
13728  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13729  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13730  }
13731  }
13732  if(ForwardEntryPtr->Command == "Sns")
13733  {
13734  if(ReverseEntryPtr->Command != "Fns")
13735  {
13736  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
13737  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
13738  TrainDataVector.clear();
13739  Utilities->CallLogPop(531);
13740  return(false);
13741  }
13742  }
13743  if(ForwardEntryPtr->Command == "Fns")
13744  {
13745  if(ReverseEntryPtr->Command != "Sns")
13746  {
13747  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
13748  " and forms a new service with headcode " + OtherHeadCode);
13749  TrainDataVector.clear();
13750  Utilities->CallLogPop(840);
13751  return(false);
13752  }
13753  else
13754  {
13755  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13756  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13757  if(OtherTrainDataPtr->Description == "")
13758  {
13759  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13760  }
13761  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13762  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13763  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13764  }
13765  }
13766  if(ForwardEntryPtr->Command == "jbo")
13767  {
13768  if(ReverseEntryPtr->Command != "Fjo")
13769  {
13770  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
13771  " and is joined by a train with headcode " + OtherHeadCode);
13772  TrainDataVector.clear();
13773  Utilities->CallLogPop(841);
13774  return(false);
13775  }
13776  }
13777  if(ForwardEntryPtr->Command == "Fjo")
13778  {
13779  if(ReverseEntryPtr->Command != "jbo")
13780  {
13781  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
13782  " and joins a train with headcode " + OtherHeadCode);
13783  TrainDataVector.clear();
13784  Utilities->CallLogPop(532);
13785  return(false);
13786  }
13787  else
13788  {
13789  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13790  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13791  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
13792  {
13793  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13794  }
13795  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
13796  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
13797  }
13798  }
13799  if(ForwardShuttleStart)
13800  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
13801  {
13802  if(!ReverseShuttleFinish)
13803  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
13804  {
13805  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
13806  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
13807  TrainDataVector.clear();
13808  Utilities->CallLogPop(1056);
13809  return(false);
13810  }
13811  }
13812  if(ReverseShuttleStart)
13813  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
13814  {
13815  if(!ForwardShuttleFinish)
13816  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
13817  {
13818  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
13819  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
13820  TrainDataVector.clear();
13821  Utilities->CallLogPop(1057);
13822  return(false);
13823  }
13824  else
13825  {
13826  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13827  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13828 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
13829  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
13830 */
13831  }
13832  }
13833  // check repeat information consistent if present
13834  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
13835  // and those not accessed here
13836 
13837  // still need to check the non-repeating links and that they have no repeats - do that outside this function
13838  bool MainRepeat = false, OtherRepeat = false;
13839  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
13840 
13841  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13842  {
13843  MainRepeat = true;
13844  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
13845  }
13846  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13847  {
13848  OtherRepeat = true;
13849  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
13850  }
13851  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
13852  {
13853  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
13854  " and the associated train with headcode " + OtherHeadCode);
13855  TrainDataVector.clear();
13856  Utilities->CallLogPop(844);
13857  return(false);
13858  }
13859  if(MainRepeat && OtherRepeat)
13860  {
13861  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
13862  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
13863  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
13864  {
13865  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
13866  " and the associated train with headcode " + OtherHeadCode);
13867  TrainDataVector.clear();
13868  Utilities->CallLogPop(845);
13869  return(false);
13870  }
13871  }
13872  Utilities->CallLogPop(863);
13873  return(true);
13874 }
13875 
13876 // ---------------------------------------------------------------------------
13877 
13878 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
13879 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
13880 {
13881  // strip spaces from extreme ends of input
13882  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
13883  if(Input == "")
13884  {
13885  Utilities->CallLogPop(856);
13886  return;
13887  }
13888  while(Input[1] == ' ')
13889  {
13890  if(Input.Length() > 1)
13891  {
13892  Input = Input.SubString(2, Input.Length() - 1);
13893  }
13894  else
13895  {
13896  Input = "";
13897  Utilities->CallLogPop(857);
13898  return;
13899  }
13900  }
13901  if(Input == "")
13902  {
13903  Utilities->CallLogPop(858);
13904  return;
13905  }
13906  while(Input[Input.Length()] == ' ')
13907  {
13908  if(Input.Length() > 1)
13909  {
13910  Input = Input.SubString(1, Input.Length() - 1);
13911  }
13912  else
13913  {
13914  Input = "";
13915  Utilities->CallLogPop(859);
13916  return;
13917  }
13918  }
13919  // now strip spaces immediately after all commas and semicolons within the text
13920  AnsiString Output = "";
13921  bool DelimiterFound = false;
13922 
13923  for(int x = 1; x < Input.Length() + 1; x++)
13924  {
13925  if(DelimiterFound)
13926  {
13927  if(Input[x] == ' ')
13928  {
13929  continue;
13930  }
13931  }
13932  if((Input[x] != ',') && (Input[x] != ';'))
13933  {
13934  DelimiterFound = false;
13935  Output = Output + Input[x];
13936  }
13937  else
13938  {
13939  DelimiterFound = true;
13940  Output = Output + Input[x];
13941  }
13942  }
13943  if(Output == "")
13944  {
13945  Input = "";
13946  Utilities->CallLogPop(860);
13947  return;
13948  }
13949  // now strip spaces immediately before all commas and semicolons within the text
13950  Input = Output;
13951  Output = "";
13952  DelimiterFound = false;
13953  for(int x = Input.Length(); x > 0; x--)
13954  {
13955  if(DelimiterFound)
13956  {
13957  if(Input[x] == ' ')
13958  {
13959  continue;
13960  }
13961  }
13962  if((Input[x] != ',') && (Input[x] != ';'))
13963  {
13964  DelimiterFound = false;
13965  Output = AnsiString(Input[x]) + Output;
13966  }
13967  else
13968  {
13969  DelimiterFound = true;
13970  Output = AnsiString(Input[x]) + Output;
13971  }
13972  }
13973  Input = Output;
13974  Utilities->CallLogPop(861);
13975 }
13976 
13977 // ---------------------------------------------------------------------------
13978 
13979 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
13980 // checks if an Snt or Snt-sh entry with zero starting speed is followed (somewhere, not necessarily immediately) by a TimeLoc & has the same LocationName
13981 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
13982 // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
13983 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
13984 // are done in this function, they must be done elsewhere.
13985 //a starting speed > 0 always returns false
13986 {
13987  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
13988  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
13989  LocationName = "";
13990  if(TDEntry.StartSpeed > 0)
13991  {
13992  Utilities->CallLogPop(1784);
13993  return(false);
13994  }
13995  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
13996  {
13997  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
13998  }
14000  {
14001  Utilities->CallLogPop(852);
14002  return(false);
14003  }
14004  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
14005  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
14006 
14007  if(LocRear != "")
14008  {
14009  LocationName = LocRear;
14010  }
14011  else
14012  {
14013  LocationName = LocFront;
14014  }
14015  if(LocationName == "")
14016  {
14017  Utilities->CallLogPop(1036);
14018  return(false);
14019  }
14020  if(AVEntry0.SignallerControl)
14021  {
14022  Utilities->CallLogPop(1773);
14023  return(true);
14024  }
14025 // here if not a signaller start entry so must be at least one more entry, and it is at a location
14026 
14027 //Ok Not ok continue
14028 
14029 //Frh if Snt Frh-sh cdt
14030 //Fns if Snt Fns-sh fsp or rsp
14031 //Fjo if Snt TimeTimeLoc jbo
14032 //F-nshs if Snt pas
14033 //TimeLoc dep Fer
14034 // TimeLoc arr
14035 
14036  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
14037  {
14038  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
14039  if(((AVEntry.Command == "Frh") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "F-nshs") || (AVEntry.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
14040  {
14041  Utilities->CallLogPop(1037);
14042  return(true);
14043  }
14044  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName == LocationName)) //will be a departure if same name- times not set yet so can't use them to confirm
14045  {
14046  Utilities->CallLogPop(2442);
14047  return(true);
14048  }
14049  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName != LocationName)) //arrival, not located
14050  {
14051  Utilities->CallLogPop(2438);
14052  return(false);
14053  }
14054  if((AVEntry.Command == "Fer") || (AVEntry.Command == "pas") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh") || (AVEntry.FormatType == TimeTimeLoc))
14055  {
14056  Utilities->CallLogPop(854);
14057  return(false);
14058  }
14059  if((AVEntry.Command == "cdt") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "jbo"))
14060  {
14061  continue;
14062  }
14063  }
14064  Utilities->CallLogPop(855);
14065  return(false);
14066 
14067 }
14068 
14069 // ---------------------------------------------------------------------------
14070 
14071 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
14072 {
14073  // checks that the new train start elements are valid - both exist & are connected, and that not
14074  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
14075  // & not starting with front on a continuation
14076  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
14077  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
14078 
14079  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
14080  if(RearPosition < 0)
14081  // error message given in GetTrackVectorPositionFromString
14082  {
14083  Utilities->CallLogPop(759);
14084  return(false);
14085  }
14086  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
14087  if(FrontPosition < 0)
14088  // error message given in GetTrackVectorPositionFromString
14089  {
14090  Utilities->CallLogPop(760);
14091  return(false);
14092  }
14093  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
14094  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
14095  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
14096 
14097  // check front & rear connected
14098  for(int x = 0; x < 4; x++)
14099  {
14100  if(RearTrackElement.Conn[x] == FrontPosition)
14101  {
14102  RearExitPos = x;
14103  break;
14104  }
14105  if(x == 3)
14106  {
14107  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
14108  Utilities->CallLogPop(762);
14109  return(false);
14110  }
14111  }
14112  // check not starting with front on a continuation
14113  if(FrontType == Continuation)
14114  {
14115  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
14116  Utilities->CallLogPop(937);
14117  return(false);
14118  }
14119  // check not starting on a level crossing
14120  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
14121  {
14122  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
14123  Utilities->CallLogPop(1951);
14124  return(false);
14125  }
14126  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
14127  {
14128  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
14129  Utilities->CallLogPop(1952);
14130  return(false);
14131  }
14132  // check if trying to start on diverging leg of points
14133  if((RearType == Points) && (RearExitPos == 3))
14134  {
14135  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
14136  Utilities->CallLogPop(936);
14137  return(false);
14138  }
14139  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
14140  {
14141  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
14142  Utilities->CallLogPop(1808);
14143  return(false);
14144  }
14145  Utilities->CallLogPop(905);
14146  return(true);
14147 }
14148 
14149 // ---------------------------------------------------------------------------
14150 
14151 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
14152 // Rear & front element validity already checked in CheckStartPositionValidity
14153 // This checks for points in correct orientation, no train at start position and not starting on a locked route
14154 {
14155  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
14156  AnsiString(RearExitPos));
14157  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
14158 
14159  if(RearTrackElement.TrackType == Continuation)
14160  {
14161  EventType = FailTrainEntry;
14162  }
14163  else
14164  {
14165  EventType = FailCreateTrain;
14166  }
14167  int FrontPosition = RearTrackElement.Conn[RearExitPos];
14168  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
14169  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
14170  TTrackType RearType = RearTrackElement.TrackType;
14171  TTrackType FrontType = FrontTrackElement.TrackType;
14172  AnsiString RearName, FrontName;
14173 
14174  if(RearTrackElement.ActiveTrackElementName != "")
14175  {
14176  RearName = RearTrackElement.ActiveTrackElementName;
14177  }
14178  else
14179  {
14180  RearName = RearTrackElement.ElementID;
14181  }
14182  if(FrontTrackElement.ActiveTrackElementName != "")
14183  {
14184  FrontName = FrontTrackElement.ActiveTrackElementName;
14185  }
14186  else
14187  {
14188  FrontName = FrontTrackElement.ElementID;
14189  }
14190  TPrefDirElement PrefDirElement; // needed for next function but not used
14191  int LockedVectorNumber; // needed for next function but not used
14192 
14193  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
14194  {
14195  if(ReportFlag)
14196  {
14197  if(EventType == FailCreateTrain)
14198  {
14199  EventType = FailCreateLockedRoute;
14200  }
14201  else
14202  {
14203  EventType = FailEnterLockedRoute;
14204  }
14205  LogActionError(47, HeadCode, "", EventType, FrontName);
14206  }
14207  Utilities->CallLogPop(940);
14208  return(false);
14209  }
14210  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
14211  {
14212  if(ReportFlag)
14213  {
14214  if(EventType == FailCreateTrain)
14215  {
14216  EventType = FailCreateLockedRoute;
14217  }
14218  else
14219  {
14220  EventType = FailEnterLockedRoute;
14221  }
14222  LogActionError(48, HeadCode, "", EventType, RearName);
14223  }
14224  Utilities->CallLogPop(1809);
14225  return(false);
14226  }
14227  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
14228  {
14229  if(ReportFlag)
14230  {
14231  LogActionError(27, HeadCode, "", EventType, RearName);
14232  }
14233  Utilities->CallLogPop(1810);
14234  return(false);
14235  }
14236  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
14237  {
14238  if(ReportFlag)
14239  {
14240  if(EventType == FailCreateTrain)
14241  {
14242  LogActionError(28, HeadCode, "", EventType, FrontName);
14243  }
14244  else
14245  {
14246  LogActionError(43, HeadCode, "", EventType, RearName);
14247  }
14248  }
14249  Utilities->CallLogPop(941);
14250  return(false);
14251  }
14252  if(RearType == Bridge)
14253  {
14254  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeTrackPos23 > -1))
14255  {
14256  if(ReportFlag)
14257  {
14258  LogActionError(29, HeadCode, "", EventType, RearName);
14259  }
14260  Utilities->CallLogPop(942);
14261  return(false);
14262  }
14263  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeTrackPos01 > -1))
14264  {
14265  if(ReportFlag)
14266  {
14267  LogActionError(30, HeadCode, "", EventType, RearName);
14268  }
14269  Utilities->CallLogPop(943);
14270  return(false);
14271  }
14272  }
14273  if(FrontType == Bridge)
14274  {
14275  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeTrackPos23 > -1))
14276  {
14277  if(ReportFlag)
14278  {
14279  if(EventType == FailCreateTrain)
14280  {
14281  LogActionError(31, HeadCode, "", EventType, FrontName);
14282  }
14283  else
14284  {
14285  LogActionError(44, HeadCode, "", EventType, RearName);
14286  }
14287  }
14288  Utilities->CallLogPop(944);
14289  return(false);
14290  }
14291  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeTrackPos01 > -1))
14292  {
14293  if(ReportFlag)
14294  {
14295  if(EventType == FailCreateTrain)
14296  {
14297  LogActionError(45, HeadCode, "", EventType, FrontName);
14298  }
14299  else
14300  {
14301  LogActionError(46, HeadCode, "", EventType, RearName);
14302  }
14303  }
14304  Utilities->CallLogPop(945);
14305  return(false);
14306  }
14307  }
14308  EventType = FailCreatePoints;
14309  if(RearType == Points)
14310  {
14311  if(RearTrackElement.Attribute == 1)
14312  {
14313  if(ReportFlag)
14314  {
14315  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
14316  }
14317  Utilities->CallLogPop(933);
14318  return(false);
14319  }
14320  }
14321  if(FrontType == Points)
14322  {
14323  if(FrontTrackElement.Attribute == 1)
14324  {
14325  if(ReportFlag)
14326  {
14327  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
14328  }
14329  Utilities->CallLogPop(934);
14330  return(false);
14331  }
14332  }
14333 
14334  //this section added at v2.9.1 to prevent entry for a train when there's a route set against it
14335  int HLoc = Track->TrackElementAt(1027, RearPosition).HLoc;
14336  int VLoc = Track->TrackElementAt(1028, RearPosition).VLoc;
14337  int ELink = Track->TrackElementAt(1029, RearPosition).Link[RearExitPos]; //if route entry corresponds to RearExitPos then it's set against the train
14338  int RouteNumber; //not used
14339  if(Track->TrackElementAt(1030, RearPosition).TrackType == Continuation)
14340  {
14341  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(8, HLoc, VLoc, ELink, RouteNumber))
14342  {
14343  EventType = FailEntryRouteSetAgainst;
14344  if(ReportFlag)
14345  {
14346  LogActionError(63, HeadCode, "", EventType, RearName);
14347  }
14348  Utilities->CallLogPop(2317);
14349  return(false);
14350  }
14351  }
14352  Utilities->CallLogPop(939);
14353  return(true);
14354 }
14355 
14356 // ---------------------------------------------------------------------------
14357 
14358 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
14359 {
14360  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
14361  "," + AnsiString(IncDigits));
14362  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
14363  {
14364  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
14365  }
14366  if(!Last2CharactersBothDigits(2, BaseHeadCode))
14367  {
14368  Utilities->CallLogPop(1893);
14369  return(BaseHeadCode);
14370  }
14371  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
14372  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
14373 
14374  while(NextRepeatDigits >= 100)
14375  {
14376  NextRepeatDigits -= 100; // rolls over after 99
14377  }
14378  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
14379 
14380  if(NextRepeatDigitsStr.Length() < 2)
14381  {
14382  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
14383  }
14384  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
14385 
14386  Utilities->CallLogPop(1365);
14387  return(NextRepeatHeadCode);
14388 }
14389 
14390 // ---------------------------------------------------------------------------
14391 
14392 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
14393 {
14394  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
14395  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
14396  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
14397  Utilities->CallLogPop(1366);
14398  return(NextRepeatTime);
14399 }
14400 
14401 // ---------------------------------------------------------------------------
14402 
14403 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
14404 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
14405 {
14406  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
14407  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
14408  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14409  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14410  int RepeatSecs = RepeatMinutes * 60;
14411 
14412  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
14413  {
14414  Utilities->CallLogPop(1367);
14415  return(false);
14416  }
14417  else
14418  {
14419  Utilities->CallLogPop(1368);
14420  return(true);
14421  }
14422 }
14423 
14424 // ---------------------------------------------------------------------------
14425 
14426 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
14427 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14428 
14429 /* Double crosslink (shuttle) table:
14430 
14431 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
14432  Code ShuttleLink- EntryPtr ShuttleLink-
14433  HeadCode EntryPtr
14434 
14435 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
14436 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
14437 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
14438 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
14439 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
14440 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
14441 
14442 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
14443 */
14444 
14445 {
14446  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
14447  NonRepeatingHeadCode);
14448  int ForwardCount = 0;
14449  int ReverseCount = 0;
14450  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
14451  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
14452  // Forward corresponds to Main, Reverse to Other
14453  TTrainDataEntry *MainTrainDataPtr = 0;
14454  TTrainDataEntry *OtherTrainDataPtr = 0;
14455 
14456  // forward check
14457  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14458  {
14459  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14460  if(TDEntry.HeadCode == MainHeadCode)
14461  {
14462  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14463  {
14464  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14465  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
14466  {
14467  MainTrainDataPtr = &TrainDataVector.at(x);
14468  ForwardEntryPtr = &AVEntry;
14469  ForwardCount++;
14470  ForwardTDVectorNumber = x;
14471  }
14472  }
14473  }
14474  }
14475  if(ForwardCount == 0)
14476  // this is an exception because the headcodes are selected in the same order as the forward check
14477  {
14478  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
14479  }
14480  if(ForwardCount > 1)
14481  {
14482  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
14483  MainHeadCode);
14484  TrainDataVector.clear();
14485  Utilities->CallLogPop(1061);
14486  return(false);
14487  }
14488  // reverse check
14489  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14490  {
14491  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14492  if(TDEntry.HeadCode == NonRepeatingHeadCode)
14493  {
14494  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14495  {
14496  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14497  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
14498  {
14499  OtherTrainDataPtr = &TrainDataVector.at(x);
14500  ReverseCount++;
14501  ReverseEntryPtr = &AVEntry;
14502  ReverseTDVectorNumber = x;
14503  }
14504  }
14505  }
14506  }
14507 
14508  if(ReverseCount == 0)
14509  {
14510  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
14511  TrainDataVector.clear();
14512  Utilities->CallLogPop(1062);
14513  return(false);
14514  }
14515  if(ReverseCount > 1)
14516  {
14517  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
14518  NonRepeatingHeadCode);
14519  TrainDataVector.clear();
14520  Utilities->CallLogPop(1063);
14521  return(false);
14522  }
14523  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
14524  {
14525  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
14526  TrainDataVector.clear();
14527  Utilities->CallLogPop(1064);
14528  return(false);
14529  }
14530  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
14531  {
14532  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
14533  TrainDataVector.clear();
14534  Utilities->CallLogPop(1065);
14535  return(false);
14536  }
14537  if(ForwardEntryPtr->LocationName == "")
14538  {
14539  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14540  ". One or other service does not have a location set");
14541  TrainDataVector.clear();
14542  Utilities->CallLogPop(1066);
14543  return(false);
14544  }
14545  if(ReverseEntryPtr->LocationName == "")
14546  {
14547  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14548  ". One or other service does not have a location set");
14549  TrainDataVector.clear();
14550  Utilities->CallLogPop(1067);
14551  return(false);
14552  }
14553  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
14554  {
14555  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
14556  " is at a different location to the referencing train " + MainHeadCode);
14557  TrainDataVector.clear();
14558  Utilities->CallLogPop(1068);
14559  return(false);
14560  }
14561  if(ForwardEntryPtr->Command == "F-nshs")
14562  // i.e. the non repeating link into the shuttle service
14563  {
14564  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
14565  {
14566  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
14567  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
14568  TrainDataVector.clear();
14569  Utilities->CallLogPop(1069);
14570  return(false);
14571  }
14572  }
14573  if(ForwardEntryPtr->Command == "Fns-sh")
14574  // i.e. the non repeating link out from the shuttle service
14575  {
14576  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
14577  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
14578  {
14579  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
14580  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
14581  TrainDataVector.clear();
14582  Utilities->CallLogPop(1070);
14583  return(false);
14584  }
14585  }
14586  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
14587  // i.e. a non repeating link to or from the shuttle service
14588  {
14589  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14590  {
14591  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
14592  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
14593  TrainDataVector.clear();
14594  Utilities->CallLogPop(1071);
14595  return(false);
14596  }
14597  }
14598 /* it's allowed to have a different description
14599  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
14600  {
14601  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
14602  {
14603  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
14604  TrainDataVector.clear();
14605  Utilities->CallLogPop(1072);
14606  return false;
14607  }
14608  }
14609 */
14610  if(ForwardEntryPtr->Command == "Sns-sh")
14611  {
14612  if(ReverseEntryPtr->Command != "F-nshs")
14613  {
14614  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
14615  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
14616  TrainDataVector.clear();
14617  Utilities->CallLogPop(1073);
14618  return(false);
14619  }
14620  }
14621  if(ForwardEntryPtr->Command == "F-nshs")
14622  {
14623  if(ReverseEntryPtr->Command != "Sns-sh")
14624  {
14625  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
14626  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
14627  TrainDataVector.clear();
14628  Utilities->CallLogPop(1074);
14629  return(false);
14630  }
14631  else
14632  {
14633  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14634  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
14635  if(OtherTrainDataPtr->Description == "")
14636  {
14637  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14638  }
14639  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14640  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14641  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14642  }
14643  }
14644  if(ForwardEntryPtr->Command == "Sns-fsh")
14645  {
14646  if(ReverseEntryPtr->Command != "Fns-sh")
14647  {
14648  SecondPassMessage(GiveMessages,
14649  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
14650  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
14651  TrainDataVector.clear();
14652  Utilities->CallLogPop(1075);
14653  return(false);
14654  }
14655  }
14656  if(ForwardEntryPtr->Command == "Fns-sh")
14657  {
14658  if(ReverseEntryPtr->Command != "Sns-fsh")
14659  {
14660  SecondPassMessage(GiveMessages,
14661  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
14662  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
14663  TrainDataVector.clear();
14664  Utilities->CallLogPop(1076);
14665  return(false);
14666  }
14667  else
14668  {
14669  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
14670  // links to the non-repeating non-shuttle linked service
14671  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14672  // needed for creating formatted timetable
14673  if(OtherTrainDataPtr->Description == "")
14674  {
14675  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14676  }
14677  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14678  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14679  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14680  }
14681  }
14682  Utilities->CallLogPop(1077);
14683  return(true);
14684 }
14685 
14686 // ---------------------------------------------------------------------------
14687 
14688 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
14689 // Forward train is the finish shuttle entry 'Fns-sh'.
14690 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
14691 {
14692  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
14693  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
14694  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14695  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14696  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
14697 
14698  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
14699  {
14700  Utilities->CallLogPop(1369);
14701  return(false);
14702  }
14703  else
14704  {
14705  Utilities->CallLogPop(1370);
14706  return(true);
14707  }
14708 }
14709 
14710 // ---------------------------------------------------------------------------
14711 
14712 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
14713 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
14714 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
14715 // don't ever need to and as designed would skip repeats.
14716 
14717 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
14718 {
14719  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
14720  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
14721  {
14722  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
14723  }
14724  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
14725  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
14726  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14727 
14728  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14729  {
14730  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14731  TrainDataVector.clear();
14732  Utilities->CallLogPop(1091);
14733  return(false);
14734  }
14735  while(LastActionCommand == "Fns")
14736  {
14737  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
14738  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14739  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14740  {
14741  SecondPassMessage(GiveMessages,
14742  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
14743  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14744  TrainDataVector.clear();
14745  Utilities->CallLogPop(1092);
14746  return(false);
14747  }
14748  }
14749  // exit the 'while' with LastActionCommand FSH-XX
14750  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
14751  {
14752  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
14753  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
14754  ". The linking of two or more shuttles is not permitted.");
14755  TrainDataVector.clear();
14756  Utilities->CallLogPop(1093);
14757  return(false);
14758  }
14759  Utilities->CallLogPop(1094);
14760  return(true);
14761 }
14762 
14763 // ---------------------------------------------------------------------------
14764 
14765 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
14766 {
14767  if(!GiveMessages)
14768  {
14769  return;
14770  }
14771  // if(ServiceReference == "") ShowMessage(Message);
14772  if(!CheckHeadCodeValidity(12, false, ServiceReference))
14773  {
14774  ShowMessage(Message);
14775  }
14776  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
14777  // false means don't give messages within the function
14778  else
14779  {
14780  ShowMessage("Service " + ServiceReference + ": " + Message);
14781  }
14782 }
14783 
14784 // ---------------------------------------------------------------------------
14785 
14786 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
14787 {
14788  if(!GiveMessages)
14789  {
14790  return;
14791  }
14792  ShowMessage(Message);
14793 }
14794 
14795 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
14796 // ---------------------------------------------------------------------------
14797 
14798 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
14799 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
14800 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
14801 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
14802 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
14803 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
14804 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
14805 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
14806 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
14807 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
14808 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
14809 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
14810 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
14811 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
14812 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
14813 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
14814 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
14815 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
14816 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
14817 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
14818 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
14819 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
14820 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
14821 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
14822 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
14823 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
14824 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
14825 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
14826 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
14827 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
14828 // FailEntryRouteSetAgainst: 06:00:10: WARNING: 2F43 can't enter railway, route set against it at entry position 57-N5 //added at v2.9.1
14829 {
14830  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
14831  AnsiString(ActionEventType) + "," + LocationID);
14832  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
14833 
14834  TDateTime ActualTime = TrainController->TTClockTime; //moved from lower down at v2.9.1
14835  AnsiString TimeAndHeadCode = Utilities->Format96HHMMSS(ActualTime) + " " + HeadCode; //added at v2.9.1 to give more info to user
14836 
14837  Prefix = " ERROR: ";
14838  if(ActionEventType == FailTrainEntry)
14839  {
14840  Prefix = " HELD: ";
14841  ErrorLog = " can't enter railway, train obstructing entry position ";
14842  WarningStr = " can't enter railway, train obstructing entry position ";
14843  Display->WarningLog(1, TimeAndHeadCode + WarningStr + LocationID);
14844  }
14845  else if(ActionEventType == FailEntryRouteSetAgainst) //added at v2.9.1
14846  {
14847  Prefix = " HELD: ";
14848  ErrorLog = " can't enter railway, route set against it at entry position ";
14849  WarningStr = " can't enter railway, route set against it at entry position ";
14850  Display->WarningLog(10, TimeAndHeadCode + WarningStr + LocationID);
14851  }
14852  else if(ActionEventType == FailCreateTrain)
14853  {
14854  Prefix = " HELD: ";
14855  ErrorLog = " can't be created, train obstructing ";
14856  WarningStr = " can't be created, train obstructing ";
14857  Display->WarningLog(2, TimeAndHeadCode + WarningStr + LocationID);
14858  }
14859  else if(ActionEventType == FailCreateLockedRoute)
14860  {
14861  Prefix = " HELD: ";
14862  ErrorLog = " can't be created on a locked route at ";
14863  WarningStr = " can't be created on a locked route at ";
14864  Display->WarningLog(4, TimeAndHeadCode + WarningStr + LocationID);
14865  }
14866  else if(ActionEventType == FailEnterLockedRoute)
14867  {
14868  Prefix = " HELD: ";
14869  ErrorLog = " can't enter on a locked route at ";
14870  WarningStr = " can't enter on a locked route at ";
14871  Display->WarningLog(5, TimeAndHeadCode + WarningStr + LocationID);
14872  }
14873  else if(ActionEventType == FailCreatePoints)
14874  {
14875  Prefix = " HELD: ";
14876  ErrorLog = " can't be created, diverging points at ";
14877  WarningStr = " can't be created, diverging points at ";
14878  Display->WarningLog(3, TimeAndHeadCode + WarningStr + LocationID);
14879  }
14880  else if(ActionEventType == FailUnexpectedExitRailway)
14881  {
14882  ErrorLog = " left railway unexpectedly at ";
14883  UnexpectedExits++;
14884  }
14885  else if(ActionEventType == FailIncorrectExit)
14886  {
14887  ErrorLog = " left railway at an incorrect exit at ";
14888  IncorrectExits++;
14889  }
14890  else if(ActionEventType == FailLocTooShort)
14891  {
14892  ErrorLog = " failed to split - location too short at ";
14893  WarningStr = " failed to split, location too short at ";
14894  Display->WarningLog(6, TimeAndHeadCode + WarningStr + LocationID);
14895  }
14896  else if(ActionEventType == FailSplitDueToOtherTrain)
14897  {
14898  Prefix = " HELD: ";
14899  ErrorLog = " unable to split - other train obstructing at ";
14900  WarningStr = " unable to split - other train obstructing at ";
14901  Display->WarningLog(7, TimeAndHeadCode + WarningStr + LocationID);
14902  }
14903  else if(ActionEventType == FailUnexpectedBuffers)
14904  {
14905  ErrorLog = " stopped at buffers unexpectedly at position ";
14906  }
14907  else if(ActionEventType == FailMissedArrival)
14908  {
14909  ErrorLog = " failed to stop at ";
14910  MissedStops++;
14911  }
14912  else if(ActionEventType == FailMissedSplit)
14913  {
14914  ErrorLog = " failed to split at ";
14916  }
14917  else if(ActionEventType == FailMissedJBO)
14918  {
14919  ErrorLog = " failed to be joined by other train at ";
14921  }
14922  else if(ActionEventType == FailMissedJoinOther)
14923  {
14924  ErrorLog = " failed to join other train at ";
14926  }
14927  else if(ActionEventType == FailMissedTerminate)
14928  {
14929  ErrorLog = " failed to terminate at ";
14931  }
14932  else if(ActionEventType == FailMissedNewService)
14933  {
14934  ErrorLog = " failed to form new service at ";
14936  }
14937  else if(ActionEventType == FailMissedExitRailway)
14938  {
14939  ErrorLog = " failed to exit railway ";
14941  }
14942  else if(ActionEventType == FailMissedChangeDirection)
14943  {
14944  ErrorLog = " failed to change direction at ";
14946  }
14947  else if(ActionEventType == FailMissedPass)
14948  {
14949  ErrorLog = " failed to pass ";
14951  }
14952  else if(ActionEventType == FailBuffersPreventingStart)
14953  {
14954  ErrorLog = " facing buffers and unable to start at ";
14955  }
14956  else if(ActionEventType == FailDerailed)
14957  {
14958  ErrorLog = " DERAILED at position ";
14959  Prefix = " DERAILMENT: ";
14960  Derailments++;
14961  }
14962  else if(ActionEventType == FailBufferCrash)
14963  {
14964  ErrorLog = " CRASHED INTO BUFFERS at ";
14965  Prefix = " CRASH: ";
14966  CrashedTrains++;
14967  }
14968  else if(ActionEventType == FailLevelCrossingCrash)
14969  {
14970  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
14971  Prefix = " CRASH: ";
14972  CrashedTrains++;
14973  }
14974  else if(ActionEventType == FailCrashed)
14975  {
14976  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
14977  Prefix = " CRASH: ";
14978  CrashedTrains++;
14979  CrashedTrains++;
14980  }
14981  else if(ActionEventType == FailSPAD)
14982  {
14983  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
14984  Prefix = " SPAD: ";
14985  SPADEvents++;
14986  }
14987  else if(ActionEventType == FailLockedRoute)
14988  {
14989  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
14990  Prefix = " SPAD RISK: ";
14991  SPADRisks++;
14992  }
14993  else if(ActionEventType == RouteForceCancelled)
14994  {
14995  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
14996  }
14997  else if(ActionEventType == WaitingForJBO)
14998  {
14999  Prefix = " WARNING: ";
15000  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
15001  WarningStr = " waiting to join " + OtherHeadCode + " at ";
15002  Display->WarningLog(8, TimeAndHeadCode + WarningStr + LocationID);
15003  }
15004  else if(ActionEventType == WaitingForFJO)
15005  {
15006  Prefix = " WARNING: ";
15007  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
15008  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
15009  Display->WarningLog(9, TimeAndHeadCode + WarningStr + LocationID);
15010  }
15011 
15012  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
15013  Display->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
15014  Utilities->CallLogPop(1371);
15015 }
15016 
15017 // ---------------------------------------------------------------------------
15018 
15020 {
15021 /*
15022  TrainDataEntry
15023  AnsiString HeadCode, Description;//null on creation
15024  int StartSpeed, MaxRunningSpeed;//both kph
15025  int RepeatNumber;
15026  TActionVector ActionVector;
15027  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
15028  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
15029 
15030  ActionVectorEntry
15031  TTimetableEntryType FormatType;
15032  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
15033  AnsiString LocationName, Command, OtherHeadCode;//null on creation
15034  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
15035  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15036  int RepeatNumber;
15037 
15038  TrainOperatingData
15039  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
15040  int TrainID;
15041  TRunningEntry RunningEntry;
15042  TDateTime StartTime;
15043  AnsiString HeadCode;
15044 */
15045  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
15046  std::ofstream OutFile("TrainData.csv");
15047 
15048  if(OutFile == 0)
15049  {
15050  ShowMessage("Output file TrainData.csv failed to open");
15051  Utilities->CallLogPop(1372);
15052  return;
15053  }
15054  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15055  {
15056  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15057  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
15058 
15059  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
15060  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
15061 
15062  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
15063  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
15064  "RepeatNumber" << '\n' << '\n';
15065  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15066  {
15067  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15068  AnsiString TimetableEntryTypeStr;
15069  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
15070  switch(AVEntry.FormatType)
15071  {
15072  case 0:
15073  {
15074  TimetableEntryTypeStr = "NoFormat";
15075  break;
15076  }
15077 
15078  case 1:
15079  {
15080  TimetableEntryTypeStr = "TimeLoc";
15081  break;
15082  }
15083 
15084  case 2:
15085  {
15086  TimetableEntryTypeStr = "TimeTimeLoc";
15087  break;
15088  }
15089 
15090  case 3:
15091  {
15092  TimetableEntryTypeStr = "TimeCmd";
15093  break;
15094  }
15095 
15096  case 4:
15097  {
15098  TimetableEntryTypeStr = "StartNew";
15099  break;
15100  }
15101 
15102  case 5:
15103  {
15104  TimetableEntryTypeStr = "TimeCmdHeadCode";
15105  break;
15106  }
15107 
15108  case 6:
15109  {
15110  TimetableEntryTypeStr = "FinRemHere";
15111  break;
15112  }
15113 
15114  case 7:
15115  {
15116  TimetableEntryTypeStr = "FNSShuttle";
15117  break;
15118  }
15119 
15120  case 8:
15121  {
15122  TimetableEntryTypeStr = "SNTShuttle";
15123  break;
15124  }
15125 
15126  case 9:
15127  {
15128  TimetableEntryTypeStr = "SNSShuttle";
15129  break;
15130  }
15131 
15132  case 10:
15133  {
15134  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
15135  break;
15136  }
15137 
15138  case 11:
15139  {
15140  TimetableEntryTypeStr = "FSHNewService";
15141  break;
15142  }
15143 
15144  case 12:
15145  {
15146  TimetableEntryTypeStr = "Repeat";
15147  break;
15148  }
15149 
15150  default:
15151  {
15152  TimetableEntryTypeStr = "Default";
15153  break;
15154  }
15155  }
15156  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
15157  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
15158  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
15159  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
15160  AVEntry.NumberOfRepeats << '\n';
15161  }
15162  OutFile << '\n';
15163  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
15164  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
15165  {
15166  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
15167  AnsiString RunningEntryStr;
15168  // NotStarted, Running, Exited
15169  switch(TOD.RunningEntry)
15170  {
15171  case 0:
15172  {
15173  RunningEntryStr = "NotStarted";
15174  break;
15175  }
15176 
15177  case 1:
15178  {
15179  RunningEntryStr = "Running";
15180  break;
15181  }
15182 
15183  case 2:
15184  {
15185  RunningEntryStr = "Exited";
15186  break;
15187  }
15188  }
15189  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
15190  }
15191  OutFile << '\n';
15192  }
15193  OutFile.close();
15194  Utilities->CallLogPop(1373);
15195 }
15196 
15197 // ---------------------------------------------------------------------------
15198 
15199 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
15200 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
15201 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
15202 {
15203  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
15204  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
15206  ShowMessage(Message);
15207  BaseTime = TDateTime::CurrentDateTime();
15208  StopTTClockFlag = false;
15209  Utilities->CallLogPop(1374);
15210 }
15211 
15212 // ---------------------------------------------------------------------------
15213 
15214 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
15215 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
15216 // from the start of the relevant vectors. Can't save the pointer values
15217 // as these will be different each time the vectors are created
15218 {
15219  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
15220  Utilities->SaveFileInt(SessionFile, TrainVector.size());
15221  for(unsigned int x = 0; x < TrainVector.size(); x++)
15222  {
15223  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
15224  }
15225  Utilities->CallLogPop(1375);
15226 }
15227 
15228 // ---------------------------------------------------------------------------
15229 
15230 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
15231 {
15232  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
15233  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
15234  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
15235  // by zero error in calculating AValue, use 1
15236  for(int x = 0; x < NumberOfTrains; x++)
15237  {
15238  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
15239  // by zero error in calculating AValue, use 1
15240  NewTrain->LoadOneSessionTrain(0, SessionFile);
15241  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
15242  // added at v2.4.0. have to include as that value not stored in session file
15243  {
15244  NewTrain->StoppedWithoutPower = true;
15245  }
15246  TrainVector.push_back(*NewTrain);
15247  LastTrainLoaded = x;
15248  }
15249  delete NewTrain;
15250  Utilities->CallLogPop(1376);
15251 }
15252 
15253 // ---------------------------------------------------------------------------
15254 
15255 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
15256 {
15257  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
15258  int NumberOfTrains;
15259 
15260  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
15261  {
15262  Utilities->CallLogPop(1377);
15263  return(false);
15264  }
15265  for(int x = 0; x < NumberOfTrains; x++)
15266  {
15267  if(!(TTrain::CheckOneSessionTrain(InFile)))
15268  {
15269  Utilities->CallLogPop(1378);
15270  return(false);
15271  }
15272  }
15273  Utilities->CallLogPop(1379);
15274  return(true);
15275 }
15276 
15277 // ---------------------------------------------------------------------------
15278 
15279 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
15280 {
15281  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
15282  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
15283  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
15284  {
15285  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
15286  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
15287  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
15288  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
15289  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
15290  }
15291  Utilities->CallLogPop(1380);
15292 }
15293 
15294 // ---------------------------------------------------------------------------
15295 
15296 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15297 {
15298  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
15299  TAllRoutes::TLockedRouteClass LockedRouteObject;
15300  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
15301 
15302  for(int x = 0; x < LockedRouteVectorSize; x++)
15303  {
15304  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15305  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
15306  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
15307  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
15308  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
15309  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
15310  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
15311  }
15312  Utilities->CallLogPop(1381);
15313 }
15314 
15315 // ---------------------------------------------------------------------------
15316 
15317 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15318 {
15319  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
15320  int LockedRouteVectorSize;
15321 
15322  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
15323  {
15324  Utilities->CallLogPop(1382);
15325  return(false);
15326  }
15327  for(int x = 0; x < LockedRouteVectorSize; x++)
15328  {
15329  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15330  {
15331  Utilities->CallLogPop(1383);
15332  return(false);
15333  }
15334  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15335  {
15336  Utilities->CallLogPop(1384);
15337  return(false);
15338  }
15339  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15340  {
15341  Utilities->CallLogPop(1385);
15342  return(false);
15343  }
15344  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15345  {
15346  Utilities->CallLogPop(1386);
15347  return(false);
15348  }
15349  if(!Utilities->CheckFileDouble(SessionFile))
15350  {
15351  Utilities->CallLogPop(1387);
15352  return(false);
15353  }
15354  }
15355  Utilities->CallLogPop(1388);
15356  return(true);
15357 }
15358 
15359 // ---------------------------------------------------------------------------
15360 
15361 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
15362 {
15363  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
15364  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
15365  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
15366  {
15367  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
15368  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
15369  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
15370  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
15371  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
15372  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
15373  }
15374  Utilities->CallLogPop(1389);
15375 }
15376 
15377 // ---------------------------------------------------------------------------
15378 
15379 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15380 {
15381  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
15382  TContinuationAutoSigEntry ContinuationAutoSigObject;
15383  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
15384 
15385  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15386  {
15387  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15388  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
15389  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
15390  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
15391  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
15392  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
15393  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
15394  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
15395  }
15396  Utilities->CallLogPop(1390);
15397 }
15398 
15399 // ---------------------------------------------------------------------------
15400 
15401 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15402 {
15403  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
15404  int ContinuationAutoSigVectorSize;
15405 
15406  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
15407  {
15408  Utilities->CallLogPop(1391);
15409  return(false);
15410  }
15411  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15412  {
15413  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15414  {
15415  Utilities->CallLogPop(1392);
15416  return(false);
15417  }
15418  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15419  {
15420  Utilities->CallLogPop(1393);
15421  return(false);
15422  }
15423  if(!Utilities->CheckFileDouble(SessionFile))
15424  {
15425  Utilities->CallLogPop(1405);
15426  return(false);
15427  }
15428  if(!Utilities->CheckFileDouble(SessionFile))
15429  {
15430  Utilities->CallLogPop(1406);
15431  return(false);
15432  }
15433  if(!Utilities->CheckFileDouble(SessionFile))
15434  {
15435  Utilities->CallLogPop(1407);
15436  return(false);
15437  }
15438  if(!Utilities->CheckFileDouble(SessionFile))
15439  {
15440  Utilities->CallLogPop(1394);
15441  return(false);
15442  }
15443  }
15444  Utilities->CallLogPop(1395);
15445  return(true);
15446 }
15447 
15448 // ---------------------------------------------------------------------------
15449 
15450 /*
15451  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
15452  {
15453  public:
15454  AnsiString Description; ///< service description
15455  AnsiString HeadCode; ///< service headcode
15456  int RepeatNumber; ///< service RepeatNumber
15457  int IncrementalMinutes; ///< Repeat separation in minutes
15458  int IncrementalDigits; ///< Repeat headcode separation
15459  int VectorPosition; ///< TrackVectorPosition for the continuation element
15460  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
15461  };
15462 
15463 
15464  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
15465  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
15466 */
15467 
15469 // build this into timetable load so session loading can use it too
15470 // being a multimap it automatically sorts in ascending EventTime order
15471 {
15472  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
15474  // need to clear as this called twice when load a session
15475  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15476  {
15477  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
15478  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
15479  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
15480 
15481  if(AVFirstEntry.Command == "Snt")
15482  // new train (no need to include Snt-sh since they can't start at a continuation)
15483  {
15486  {
15488  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
15489  // retains this value for all repeats
15490  CTEEntry.RepeatNumber = 0; // for first entry
15491  CTEEntry.TrainDataEntryPtr = &TDEntry;
15492  // retains this value for all repeats
15493  CTEEntry.HeadCode = TDEntry.HeadCode;
15494  CTEEntry.Description = TDEntry.Description;
15495  CTEEntry.IncrementalMinutes = 0;
15496  CTEEntry.IncrementalDigits = 0;
15497  if(AVLastEntry.FormatType == Repeat)
15498  {
15499  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
15500  // retains this value or 0 for all repeats
15501  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
15502  // retains this value or 0 for all repeats
15503  }
15504  CTEMMP.first = AVFirstEntry.EventTime;
15505  CTEMMP.second = CTEEntry;
15506  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15507  // base entry
15508  if(TDEntry.NumberOfTrains > 1)
15509  {
15510  if(AVLastEntry.FormatType != Repeat)
15511  {
15512  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
15513  }
15514  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
15515  {
15516  CTEEntry.RepeatNumber = y;
15517  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
15518  // CTEEntry.VectorPosition stays same
15519  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
15520  CTEMMP.second = CTEEntry;
15521  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15522  }
15523  }
15524  }
15525  }
15526  }
15527  Utilities->CallLogPop(1396);
15528 }
15529 
15530 // ---------------------------------------------------------------------------
15531 
15533 {
15534  // called when WarningFlashCount == 0 or when press zoomout button
15535  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
15536  if(!Display->ZoomOutFlag)
15537  {
15538  Utilities->CallLogPop(1156);
15539  return;
15540  }
15541  for(unsigned int x = 0; x < TrainVector.size(); x++)
15542  {
15543  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
15544  // if OldPlotElement[x] == -1 then ignore (not plotted)
15546  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
15547  }
15548  Display->Update();
15549  // need to keep this since Update() not called for PlotSmallOutput as too slow
15550  Utilities->CallLogPop(742);
15551 }
15552 
15553 // ---------------------------------------------------------------------------
15554 
15555 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
15556 {
15557  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
15558  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
15559  {
15560  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
15561  }
15562  Utilities->CallLogPop(740);
15563  return(TrainVector.at(VecPos));
15564 }
15565 
15566 // ---------------------------------------------------------------------------
15567 
15568 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
15569 {
15570  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
15571  AnsiString RetStr = "", PartStr = "";
15572 
15573 
15574 /*
15575  Have description & mass etc for train at top - header, then array of actions
15576 
15577  class TActionVectorEntry
15578  {
15579  public:
15580  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
15582  bool SignallerControl;
15584  bool Warning;
15586  int NumberOfRepeats;
15588  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15590  TDateTime EventTime, ArrivalTime, DepartureTime;
15592  TNumList ExitList;
15594  TTimetableFormatType FormatType;
15596  TTimetableLocationType LocationType;
15598  TTimetableSequenceType SequenceType;
15600  TTimetableShuttleLinkType ShuttleLinkType;
15602  TTrainDataEntry *LinkedTrainEntryPtr;
15604  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
15606 
15607  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
15608 
15609  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
15610 
15611  class TTrainOperatingData
15612  {
15613  public:
15614  int TrainID;
15615  TActionEventType EventReported;
15616  TRunningEntry RunningEntry;
15617 
15618  //inline function
15619  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
15620  };
15621 
15622  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
15623 
15624  class TTrainDataEntry
15625  {
15626  public:
15627  AnsiString HeadCode, ServiceReference, Description;
15629  double MaxBrakeRate;
15631  double MaxRunningSpeed;
15633  double PowerAtRail;
15635  int Mass;
15637  int NumberOfTrains;
15639  int SignallerSpeed;
15641  int StartSpeed;
15643  TActionVector ActionVector;
15645  TTrainOperatingDataVector TrainOperatingDataVector;
15647 
15648  //inline function
15649  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
15650  };
15651 
15652  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
15653 
15654  //formatted timetable types
15655  class TOneTrainFormattedEntry
15656  {
15657  AnsiString Action;//includes location if relevanr
15658  AnsiString Time;
15659  };
15660 
15661  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
15662 
15663  class TOneCompleteFormattedTrain//headcode + list of actions
15664  {
15665  public:
15666  AnsiString HeadCode;
15667  TOneFormattedTrainVector OneFormattedTrainVector;
15668  };
15669 
15670  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
15671 
15672  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
15673  {
15674  public:
15675  AnsiString Header;//description, mass, power, brake rate etc
15676  int NumberOfTrains;// number of repeats + 1
15677  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
15678  };
15679 
15680 
15681  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
15682  //end of formatted timetable types
15683 
15684 */
15685 
15686  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15687 
15688  // format "16/06/2009 20:55:17"
15689  // avoid characters in filename:= / \ : * ? " < > |
15690  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
15691 
15692  AnsiString ShortTTName = "";
15693 
15694  for(int x = TTFileName.Length(); x > 0; x--)
15695  {
15696  if(TTFileName[x] == '\\')
15697  {
15698  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
15699  break;
15700  }
15701  }
15702 
15703  ShowMessage("Creates two timetables named " + ShortTTName +
15704  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
15705 
15706  Screen->Cursor = TCursor(-11); // Hourglass
15707 
15708  AnsiString FormatNoDPStr = "#######0";
15709  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
15710 
15712  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
15713  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
15714 
15715  // all timetable in formatted form
15716  //create the AllTTTrains vector
15717  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15718  {
15719  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
15720  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
15721  if(TrainDataEntry.Mass > 0)
15722  {
15723  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
15724  }
15725  if(TrainDataEntry.PowerAtRail > 0)
15726  {
15727  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
15728  }
15729  if(TrainDataEntry.MaxBrakeRate > 0)
15730  {
15731  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
15732  }
15733  if(TrainDataEntry.MaxRunningSpeed > 0)
15734  {
15735  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
15736  }
15737  FirstHeadCode = TrainDataEntry.HeadCode;
15738  int IncDigits = 0, IncMinutes = 0;
15739  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15740  if(!ActionVector.empty())
15741  {
15742  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
15743  {
15744  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
15745  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15746  }
15747  }
15748  TTrainFormattedInformation OneTTLine;
15749  // contains all information for a single TT entry (including repeats)
15750  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
15751  {
15752  OneTTLine.Header = "";
15753  if((TrainDataEntry.Description != "") && (MassStr != ""))
15754  {
15755  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
15756  }
15757  else if(TrainDataEntry.Description != "")
15758  {
15759  OneTTLine.Header = TrainDataEntry.Description;
15760  }
15761  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
15762  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
15763  for(unsigned int z = 0; z < ActionVector.size(); z++)
15764  {
15765  TOneTrainFormattedEntry OneTTEntry;
15766  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
15767  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
15768  AnsiString PartStr = "", TimeStr = "";
15769 /*
15770  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
15771  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
15772  ExitRailway};
15773  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
15774  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
15775  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
15776 */
15777  if(ActionVectorEntry.SequenceType == Start)
15778  {
15779  if(ActionVectorEntry.FormatType == StartNew)
15780  {
15781  if(ActionVectorEntry.LocationName != "")
15782  {
15783  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15784  {
15785  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15786  }
15787  else
15788  {
15789  PartStr = "Created at " + ActionVectorEntry.LocationName;
15790  }
15791  }
15792  else // may be a named continuation or other element, and if so report that
15793  {
15794  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
15795  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15796  {
15797  if(LocName != "")
15798  {
15799  PartStr = "Enters at " + LocName;
15800  }
15801  else // use rear position if it's a continuation
15802  {
15803  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15804  }
15805  }
15806  else // not a continuation
15807  {
15808  if(LocName != "")
15809  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
15810  // but include anyway
15811  {
15812  PartStr = "Created at " + LocName;
15813  }
15814  else // use rear position again
15815  {
15816  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15817  }
15818  }
15819  }
15820  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
15821  }
15822  else if(ActionVectorEntry.FormatType == SNTShuttle)
15823  {
15824  if(y == 0) // first train
15825  {
15826  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15827  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
15828  }
15829  else
15830  {
15831  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15832  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15833  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
15834  } // y-1 for headcode above since it is the last repeat value that the train is from
15835 
15836  }
15837  else if(ActionVectorEntry.Command == "Sfs")
15838  {
15839  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
15840  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15841  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
15842  }
15843  else if(ActionVectorEntry.Command == "Sns")
15844  {
15845  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15846  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15847  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
15848  }
15849  else if(ActionVectorEntry.FormatType == SNSShuttle)
15850  {
15851  if(y == 0) // first entry from shuttle
15852  {
15853  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15854  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
15855  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
15856  }
15857  else
15858  {
15859  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15860  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15861  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
15862  } // y-1 for headcode above since it is the last repeat value that the train is from
15863 
15864  }
15865  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
15866  {
15867  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15868  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
15869  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
15870  AnsiString FirstHeadCode = TDE->HeadCode;
15871  int LastRepeatNumber = TDE->NumberOfTrains - 1;
15872  // a shuttle has to have at least 1 repeat
15873  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
15874  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
15875  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
15876  }
15877  }
15878  else if(ActionVectorEntry.SequenceType == Intermediate)
15879  {
15880  if(ActionVectorEntry.FormatType == TimeTimeLoc)
15881  {
15882  // here need 2 entries if times different so push the first right away & the second later
15883  // if times same just give the arrival entry
15884  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
15885  {
15886  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15887  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15888  OneTTEntry.Action = PartStr;
15889  OneTTEntry.Time = TimeStr;
15890  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
15891  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15892  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
15893  }
15894  else
15895  {
15896  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
15897  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15898  }
15899  }
15900  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
15901  {
15902  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15903  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15904  }
15905  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
15906  {
15907  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15908  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
15909  }
15910  else if(ActionVectorEntry.FormatType == PassTime)
15911  {
15912  PartStr = "Passes " + ActionVectorEntry.LocationName;
15913  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
15914  }
15915  else if(ActionVectorEntry.Command == "jbo")
15916  {
15917  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
15918  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15919  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
15920  }
15921  else if(ActionVectorEntry.Command == "fsp")
15922  {
15923  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
15924  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15925  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
15926  }
15927  else if(ActionVectorEntry.Command == "rsp")
15928  {
15929  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
15930  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15931  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
15932  }
15933  else if(ActionVectorEntry.Command == "cdt")
15934  {
15935  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
15936  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
15937  }
15938  }
15939  else if(ActionVectorEntry.SequenceType == Finish)
15940  {
15941  if(ActionVectorEntry.Command == "Fns")
15942  {
15943  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15944  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15945  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
15946  }
15947  else if(ActionVectorEntry.Command == "F-nshs")
15948  {
15949  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15950  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15951  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
15952  }
15953  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15954  {
15955  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
15956  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15957  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
15958  // y+1 because it's the NEXT service repeat number that is relevant
15959  }
15960  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15961  {
15962  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15963  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15964  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
15965  }
15966  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15967  {
15968  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15969  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15970  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
15971  // y+1 because it's the NEXT service repeat number that is relevant
15972  }
15973  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15974  {
15975  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
15976  // only used in chronological tt
15977  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
15978  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
15979  }
15980  else if(ActionVectorEntry.Command == "Frh")
15981  {
15982  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
15983  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
15984  if(z > 0)
15985  // should be for finish entry but include check for safety
15986  {
15987  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
15988  {
15989  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
15990  }
15991  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
15992  {
15993  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
15994  }
15995  else
15996  {
15997  TimeStr = " "; // shouldn't ever get here
15998  }
15999  }
16000  }
16001  else if(ActionVectorEntry.Command == "Fer")
16002  {
16003  AnsiString AllowedExits;
16004  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
16005  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
16006  }
16007  else if(ActionVectorEntry.Command == "Fjo")
16008  {
16009  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
16010  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16011  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
16012  }
16013  }
16014  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
16015  {
16016  continue; // no entry needed for a repeat
16017  }
16018  OneTTEntry.Action = PartStr;
16019  OneTTEntry.Time = TimeStr;
16020  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
16021  // one per action
16022  }
16023  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
16024  // one per repeat
16025  }
16026  AllTTTrains->push_back(OneTTLine); // one per repeating train
16027  }
16028  // AllTTTrains vector now complete
16029 
16030  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
16031 
16032  if(TTFile == 0)
16033  {
16034  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
16035  delete AllTTTrains;
16036  Utilities->CallLogPop(1567);
16037  return;
16038  }
16039 /* formatted timetable types
16040  class TOneTrainFormattedEntry
16041  {
16042  AnsiString Action;//includes location if relevant
16043  AnsiString Time;
16044  };
16045 
16046  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
16047 
16048  class TOneCompleteFormattedTrain//headcode + list of actions
16049  {
16050  public:
16051  AnsiString HeadCode;
16052  TOneFormattedTrainVector OneFormattedTrainVector;
16053  };
16054 
16055  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
16056 
16057  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
16058  {
16059  public:
16060  AnsiString Header;//description, mass, power, brake rate etc
16061  int NumberOfTrains;// number of repeats + 1
16062  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
16063  };
16064 
16065  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
16066  //end of formatted timetable types
16067 */
16068 
16069  // new layout using multiple rows
16070  TTFile << TableTitle.c_str() << '\n' << '\n';
16071  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
16072  {
16073  TTFile << AllTTTrains->at(x).Header.c_str();
16074  TTFile << '\n';
16075  TTFile << ','; // for the blank line above the action list
16076  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16077  {
16078  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
16079  {
16080  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
16081  }
16082  else
16083  {
16084  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
16085  }
16086  }
16087  TTFile << '\n' << '\n';
16088 
16089  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
16090  {
16091  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
16092  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16093  {
16094  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
16095  {
16096  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
16097  }
16098  else
16099  {
16100  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
16101  }
16102  }
16103  TTFile << '\n';
16104  }
16105  TTFile << '\n' << '\n';
16106  }
16107 
16108  TTFile.close();
16109 
16110  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16111 
16112  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16113 
16114  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
16115 
16116  if(TTFile2 == 0)
16117  {
16118  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
16119  delete AllTTTrains;
16120  Utilities->CallLogPop(1710);
16121  return;
16122  }
16123  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
16124  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
16125  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
16126 
16127  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
16128  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
16129 
16130  // multimap of AnsiStrings with TimeString as key (to sort automatically)
16131 
16132  TTFile2 << TableTitle.c_str() << '\n' << '\n';
16133  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
16134  {
16135  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16136  {
16137  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
16138  {
16139  bool GiveMessagesFalse = false;
16140  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
16141  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
16142  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
16143  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
16144  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
16145  {
16146  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
16147  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
16148  TimeString = TimeString.SubString(9, 5);
16149  ActionString += " " + OtherHeadCode;
16150  }
16151  if(TimeString.SubString(1, 7) == "End at ")
16152  // for Frh-sh final entry
16153  {
16154  TimeString = TimeString.SubString(8, 5);
16155  }
16156  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
16157  AnsiMultiMapEntry.first = TimeString;
16158  AnsiMultiMapEntry.second = OneLine;
16159  TAMM->insert(AnsiMultiMapEntry);
16160  }
16161  }
16162  }
16163 
16164  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
16165  {
16166  TTFile2 << (AMMIT->second).c_str();
16167  }
16168  delete AllTTTrains;
16169  delete TAMM;
16170  TTFile2.close();
16171  Utilities->CallLogPop(1580);
16172 }
16173 
16174 // ---------------------------------------------------------------------------
16175 
16176 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
16177  bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
16178 {
16179 
16180  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
16181  bool AnalysisError = false;
16182  AnsiString SequenceLog = "SequenceLog\n";
16183 
16184 /* Double crosslink (shuttle) table:
16185 
16186 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
16187  Code ShuttleLink- EntryPtr ShuttleLink-
16188  HeadCode EntryPtr
16189 
16190 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
16191 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
16192 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
16193 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
16194 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
16195 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
16196 
16197 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
16198 */
16199 
16200  try
16201  {
16202  //New section at v2.5.0 for tt conflict analysis
16203  /*
16204  typedef std::list<AnsiString> TServiceCallingLocsList;
16205  typedef std::map<AnsiString, TServiceCallingLocsList> TAllServiceCallingLocsMap;
16206 
16208  struct TLocServiceTimes
16209  {
16210  AnsiString Location;
16211  AnsiString ServiceAndRepeatNum;
16212  AnsiString AtLocTime;
16213  AnsiString ArrTime;
16214  AnsiString DepTime;
16215  AnsiString FrhMarker;
16216  };
16217  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16218  */
16219 
16220  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
16221  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
16222 
16223  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
16224  TTrainDataVector::iterator TDVIt, TDVCopyIt;
16225  int Suffix = 0;
16226  int IteratorNumber = 0;
16227  AnsiString AnsiSuffix = "";
16228  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
16229  {
16230  IteratorNumber++; //first value in loop is 1
16231  Suffix = 0;
16232  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
16233  {
16234  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
16235  {
16236  Suffix++; //first value is 1
16237  AnsiSuffix = AnsiString(Suffix);
16238  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
16239  }
16240  }
16241  }
16242  SequenceLog += "1\n";
16243  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
16244  TServiceCallingLocsList ServiceCallingLocsList;
16245  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
16246  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16247  {
16248  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16249  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16250  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
16251  ServiceCallingLocsList.clear();
16252  if(ActionVector.empty())
16253  {
16254  continue;
16255  }
16256  if(ActionVector.at(0).SignallerControl)
16257  {
16258  continue;
16259  }
16260  for(unsigned int z = 0; z < ActionVector.size(); z++)
16261  {
16262  TActionVectorEntry AVE = ActionVector.at(z);
16263  if(AVE.FormatType == StartNew)
16264  {
16265  if(AVE.LocationType == AtLocation) //located Snt
16266  {
16267  ServiceCallingLocsList.push_back(AVE.LocationName);
16268  }
16269  else //unlocated Snt (could be entering at continuation)
16270  {
16272  if(TE.ActiveTrackElementName != "")
16273  {
16274  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
16275  }
16276  else
16277  {
16278  int HLoc = TE.HLoc;
16279  int VLoc = TE.VLoc;
16280  AnsiString HString;
16281  AnsiString VString;
16282  if(HLoc < 0)
16283  {
16284  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16285  }
16286  else
16287  {
16288  HString = AnsiString(HLoc);
16289  }
16290  if(VLoc < 0)
16291  {
16292  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16293  }
16294  else
16295  {
16296  VString = AnsiString(VLoc);
16297  }
16298  ServiceCallingLocsList.push_back(HString + '-' + VString);
16299  }
16300  }
16301  }
16302  else if(AVE.SequenceType == Start) //other start entries, all located
16303  {
16304  ServiceCallingLocsList.push_back(AVE.LocationName);
16305  }
16306  else if(AVE.FormatType == TimeLoc) //z must be > 0
16307  {
16308  if(ServiceCallingLocsList.back() != AVE.LocationName)
16309  {
16310  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
16311  }
16312  }
16313  else if(AVE.FormatType == PassTime)
16314  {
16315  ServiceCallingLocsList.push_back(AVE.LocationName);
16316  }
16317  else if(AVE.FormatType == TimeTimeLoc)
16318  {
16319  ServiceCallingLocsList.push_back(AVE.LocationName);
16320  }
16321  else if(AVE.Command == "cdt") //list if not next to start or finish
16322  {
16323  if(ActionVector.at(z-1).SequenceType == Start)
16324  {
16325  continue;
16326  }
16327  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
16328  {
16329  continue;
16330  }
16331  else
16332  {
16333  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
16334  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
16335  }
16336  }
16337  else if(AVE.FormatType == ExitRailway) //Fer
16338  {
16339  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
16340  AnsiString LName = TE.ActiveTrackElementName;
16341  if(LName != "")
16342  {
16343  ServiceCallingLocsList.push_back(LName);
16344  }
16345  else
16346  {
16347  int HLoc = TE.HLoc;
16348  int VLoc = TE.VLoc;
16349  AnsiString HString;
16350  AnsiString VString;
16351  if(HLoc < 0)
16352  {
16353  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16354  }
16355  else
16356  {
16357  HString = AnsiString(HLoc);
16358  }
16359  if(VLoc < 0)
16360  {
16361  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16362  }
16363  else
16364  {
16365  VString = AnsiString(VLoc);
16366  }
16367  ServiceCallingLocsList.push_back(HString + '-' + VString);
16368  }
16369  }
16370  }
16371  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
16372  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
16373  }
16374  //AllServiceCallingLocsMap built
16375  SequenceLog += "2\n";
16376  //test validity of AllServiceCallingLocsMap
16377 /*
16378  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16379  std::ofstream Test(TestFile.c_str());
16380 
16381  if(TestFile == 0)
16382  {
16383  ShowMessage("TestFile failed to open - can't be created");
16384  Utilities->CallLogPop();
16385  return false;
16386  }
16387 
16388  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
16389  {
16390  Test << ASCLIt->first << '\n'; //service ref
16391  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
16392  {
16393  Test << *SCLIt << '\n';
16394  }
16395  Test << "\n\n";
16396  }
16397  Test.close();
16398  Utilities->CallLogPop();
16399  return true;
16400 */
16401 
16402  //initialise variables before calc LastTTTime & build LocServiceTimesVector
16403  if(TrainDataVector.empty())
16404  {
16405  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
16406  Utilities->CallLogPop(2209);
16407  return(false);
16408  }
16409  TLocServiceTimes TLSTEntry;
16410  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
16411  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
16412  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
16413  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
16414  LastTTTime = "";
16415  SequenceLog += "3\n";
16416  //calculate LastTTTime
16417  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16418  {
16419  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16420  TActionVector &ActionVector = TrainDataEntry.ActionVector;
16421  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
16422  TDateTime LastTDTime;
16423  int IncMinutes = 0;
16424  NumTrains = TrainDataEntry.NumberOfTrains;
16425  if(ActionVector.empty())
16426  {
16427  continue;
16428  }
16429  if(ActionVector.at(0).SignallerControl)
16430  {
16431  continue;
16432  }
16433  if(AVLast->FormatType == Repeat)
16434  {
16435  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16436  AVLast--; //now points to the command before the repeat
16437  }
16438  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
16439  {
16440  AVLast--; //points to last timed entry
16441  }
16442  //here AVLast points to last entry with a time
16443  if(AVLast->ArrivalTime != TDateTime(-1))
16444  {
16445  LastTDTime = AVLast->ArrivalTime;
16446  }
16447  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
16448  {
16449  LastTDTime = AVLast->EventTime;
16450  }
16451  else
16452  {
16453  continue; //shouldn't ever reach here but if do then skip this service
16454  }
16455  if(NumTrains == 1)
16456  {
16457  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
16458  }
16459  else
16460  {
16461  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
16462  }
16463  if(LastAnsiTime > LastTTTime)
16464  {
16465  LastTTTime = LastAnsiTime;
16466  }
16467  }
16468  SequenceLog += "4\n";
16469 //build LocServiceTimesVector
16470 
16471 /*
16472  struct TLocServiceTimes
16473  {
16474  AnsiString Location;
16475  AnsiString ServiceAndRepeatNum;
16476  AnsiString AtLocTime;
16477  AnsiString ArrTime;
16478  AnsiString DepTime;
16479  AnsiString FrhMarker;
16480  };
16481  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16482 
16483 This works as follows:
16484 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
16485 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
16486 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
16487 
16488 Every action for every train is examined and times entered as follows:-
16489 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
16490 b) an unlocated Snt: entry time becomes DepTime
16491 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
16492 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
16493 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
16494 f) TimeTimeLoc: Arrival time entered as ArrTime, a check if Arr & Dep same and if so go in as one entry, else all minutes between entered as AtLocs then DepTime
16495 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
16496 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
16497 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
16498 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
16499 */
16500  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16501  {
16502  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16503  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16504  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
16505  int IncMinutes = 0;
16506  NumTrains = TrainDataEntry.NumberOfTrains;
16507  if(ActionVector.empty())
16508  {
16509  continue;
16510  }
16511  if(ActionVector.at(0).SignallerControl)
16512  {
16513  continue;
16514  }
16515  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
16516  {
16517  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16518  }
16519  for(int y = 0; y < NumTrains; y++) //y is the repeat number
16520  {
16521  if(NumTrains == 1)
16522  {
16523  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
16524  }
16525  else if(y == 0)
16526  {
16527  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
16528  }
16529  else
16530  {
16531  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
16532  }
16533  for(unsigned int z = 0; z < ActionVector.size(); z++)
16534  {
16535  TActionVectorEntry AVE = ActionVector.at(z);
16536  TLSTEntry.AtLocTime = "";
16537  TLSTEntry.ArrTime = "";
16538  TLSTEntry.DepTime = "";
16539  TLSTEntry.Location = "";
16540  TLSTEntry.FrhMarker = "";
16541 
16542  if(AVE.FormatType == StartNew) //Snt only
16543  {
16544  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
16545  {
16546  TLSTEntry.Location = AVE.LocationName;
16547  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
16548  LocServiceTimesVector.push_back(TLSTEntry);
16549 
16550  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16551  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
16552  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16553  {
16554  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16555  {
16556  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
16557  break;
16558  }
16559  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16560  {
16561  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
16562  break;
16563  }
16564  }
16565  if(FoundStopTime == "")
16566  {
16567  throw Exception("Failure to determine FoundStopTime for located Snt");
16568  }
16569  int WhileCount = 0;
16570  while(true)
16571  {
16572  //add minutes until reach FoundStopTime but don't add that time
16573  WhileCount++;
16574  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16575  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16576  TLSTEntry.DepTime = "";
16577  TLSTEntry.ArrTime = "";
16578  if(IncTime >= FoundStopTime) //don't add that time
16579  {
16580  break;
16581  }
16582  LocServiceTimesVector.push_back(TLSTEntry);
16583  if(WhileCount > 2000)
16584  {
16585  throw Exception("While loop failed to break in 2000 loops for located Snt");
16586  }
16587  }
16588  }
16589  else //unlocated Snt, use the EventTime as DepTime for this vector
16590  {
16592  if(TE.ActiveTrackElementName != "")
16593  {
16594  TLSTEntry.Location = TE.ActiveTrackElementName;
16595  }
16596  else
16597  {
16598  int HLoc = TE.HLoc;
16599  int VLoc = TE.VLoc;
16600  AnsiString HString;
16601  AnsiString VString;
16602  if(HLoc < 0)
16603  {
16604  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16605  }
16606  else
16607  {
16608  HString = AnsiString(HLoc);
16609  }
16610  if(VLoc < 0)
16611  {
16612  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16613  }
16614  else
16615  {
16616  VString = AnsiString(VLoc);
16617  }
16618  TLSTEntry.Location = HString + '-' + VString;
16619  }
16620  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
16621  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16622  LocServiceTimesVector.push_back(TLSTEntry);
16623  }
16624  }
16625 
16626  else if(AVE.SequenceType == Start) //other start entries, all located
16627  {
16628  TLSTEntry.Location = AVE.LocationName;
16629  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
16630  LocServiceTimesVector.push_back(TLSTEntry);
16631  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16632  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16633  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16634  {
16635  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16636  {
16637  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
16638  break;
16639  }
16640  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16641  {
16642  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
16643  break;
16644  }
16645  }
16646  if(FoundStopTime == "")
16647  {
16648  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16649  }
16650  int WhileCount = 0;
16651  while(true)
16652  {
16653  //add minutes until reach FoundStopTime but don't add that time
16654  WhileCount++;
16655  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16656  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16657  TLSTEntry.DepTime = "";
16658  TLSTEntry.ArrTime = "";
16659  if(IncTime >= FoundStopTime) //don't add that time
16660  {
16661  break;
16662  }
16663  LocServiceTimesVector.push_back(TLSTEntry);
16664  if(WhileCount > 2000)
16665  {
16666  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16667  }
16668  }
16669  }
16670 
16671  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if arrival add in all mins to the departure or finish
16672  {
16673  TLSTEntry.Location = AVE.LocationName;
16674  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
16675  {
16676  bool SkipAddingMinutes = false;
16677  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
16678  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16679  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
16680  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16681  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16682  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16683  {
16684  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16685  {
16686  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
16687  break;
16688  }
16689  if(ActionVector.at(a).SequenceType == Finish) //finish catered for in a later test
16690  {
16691  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
16692  if((a <= (z + 2)) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
16693  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
16694  //at v2.10.0 changed (a == z + 1) to (a <= (z + 2)) as can have a cdt between, this allows for that
16695  {
16696  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
16697  SkipAddingMinutes = true;
16698  }
16699  break;
16700  }
16701  }
16702  if(FoundStopTime == "")
16703  {
16704  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16705  }
16706  if(!SkipAddingMinutes)
16707  {
16708  int WhileCount = 0;
16709  while(true)
16710  {
16711  //add minutes until reach FoundStopTime but don't add that time
16712  WhileCount++;
16713  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16714  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16715  TLSTEntry.DepTime = "";
16716  TLSTEntry.ArrTime = "";
16717  if(IncTime >= FoundStopTime) //don't add that time
16718  {
16719  break;
16720  }
16721  LocServiceTimesVector.push_back(TLSTEntry);
16722  if(WhileCount > 2000)
16723  {
16724  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16725  }
16726  }
16727  }
16728  }
16729  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
16730  {
16731  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
16732  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16733  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
16734  {
16735  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
16736  {
16737  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
16738  LocServiceTimesVector.pop_back();
16739  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
16740  }
16741  else //just add the dep & atloc times
16742  {
16743  TLSTEntry.ArrTime = "";
16744  LocServiceTimesVector.push_back(TLSTEntry);
16745  }
16746  }
16747  else //just add the dep & atloc times
16748  {
16749  TLSTEntry.ArrTime = "";
16750  LocServiceTimesVector.push_back(TLSTEntry);
16751  }
16752  }
16753  }
16754 
16755  else if(AVE.FormatType == TimeTimeLoc)
16756  {
16757  TLSTEntry.Location = AVE.LocationName;
16758  if(AVE.ArrivalTime > TDateTime(-1)) //should be
16759  {
16760  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
16761  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16762  }
16763  if(AVE.DepartureTime > TDateTime(-1)) //should be
16764  {
16765  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
16766  }
16767  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
16768  {
16769  LocServiceTimesVector.push_back(TLSTEntry);
16770  }
16771  else
16772  {
16773  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
16774  TLSTEntry.DepTime = "";
16775  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
16776  TLSTEntry.ArrTime = ""; //done with this now
16777  while(TLSTEntry.AtLocTime < TempDepTime)
16778  {
16779  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16780  if(TLSTEntry.AtLocTime == TempDepTime)
16781  {
16782  TLSTEntry.DepTime = TempDepTime; //restore value
16783  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
16784  }
16785  else
16786  {
16787  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
16788  }
16789  }
16790  }
16791  }
16792 
16793  else if(AVE.FormatType == PassTime) //added at v2.9.1
16794  { //adds 2 entries, 1st with PassTime as ArrTime and AtLocTime, 2nd with PassTime as AtLocTime & DepTime
16795  TLSTEntry.Location = AVE.LocationName;;
16796  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(73, AVE.EventTime, y, IncMinutes));
16797  TLSTEntry.ArrTime = TLSTEntry.AtLocTime; //DepTime already set to null
16798  LocServiceTimesVector.push_back(TLSTEntry); //1st entry
16799  TLSTEntry.ArrTime = ""; //need to reset this to null
16800  TLSTEntry.DepTime = TLSTEntry.AtLocTime;
16801  LocServiceTimesVector.push_back(TLSTEntry); //2nd entry
16802  }
16803 
16804  else if(AVE.FormatType == ExitRailway) //Fer
16805  {
16806  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
16807  //don't know which exit will be used during operation so use the first in ExitList, if several with different names then will
16808  //be wrong, but can't guess from here & most will have same name
16809  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
16810  if(LName != "")
16811  {
16812  TLSTEntry.Location = LName;
16813  }
16814  else
16815  {
16816  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
16817  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
16818  AnsiString HString;
16819  AnsiString VString;
16820  if(HLoc < 0)
16821  {
16822  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16823  }
16824  else
16825  {
16826  HString = AnsiString(HLoc);
16827  }
16828  if(VLoc < 0)
16829  {
16830  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16831  }
16832  else
16833  {
16834  VString = AnsiString(VLoc);
16835  }
16836  TLSTEntry.Location = HString + '-' + VString;
16837  }
16838  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
16839  }
16840 
16841  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
16842  {
16843  AnsiString FrhTime;
16844  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
16845  {
16846  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
16847  }
16848  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
16849  {
16850  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
16851  }
16852  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
16853  TLSTEntry.Location = AVE.LocationName;
16854  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16855  TLSTEntry.FrhMarker = "Frh";
16856  LocServiceTimesVector.push_back(TLSTEntry);
16857  TLSTEntry.FrhMarker = "";
16858  //add all times from next minute to end of timetable
16859  while(IncTime <= LastTTTime)
16860  {
16861  TLSTEntry.AtLocTime = IncTime;
16862  LocServiceTimesVector.push_back(TLSTEntry);
16863  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16864  }
16865  }
16866 
16867  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
16868  {
16869  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
16870  {
16871  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
16872  TLSTEntry.Location = AVE.LocationName;
16873  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16874  TLSTEntry.FrhMarker = "Frh";
16875  LocServiceTimesVector.push_back(TLSTEntry);
16876  TLSTEntry.FrhMarker = "";
16877  //add all times from next minute to end of timetable
16878  while(IncTime <= LastTTTime)
16879  {
16880  TLSTEntry.AtLocTime = IncTime;
16881  LocServiceTimesVector.push_back(TLSTEntry);
16882  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16883  }
16884  }
16885  }
16886 
16887  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
16888  {
16889  //nothing is done here as the entry will be listed at this time under the new service reference
16890  }
16891  }
16892  }
16893  }
16894  SequenceLog += "5\n";
16895  //now sort in location order
16896  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
16897  //LocServiceTimesVector now complete & sorted in location order
16898 
16900 /*
16901  std::ofstream LSTVFile("LSTVFile.txt");
16902  for(TLocServiceTimesVector::iterator LSTVIt = LocServiceTimesVector.begin(); LSTVIt != LocServiceTimesVector.end(); LSTVIt++)
16903  {
16904  LSTVFile << LSTVIt->Location + '\n';
16905  LSTVFile << LSTVIt->ServiceAndRepeatNum + '\n';
16906  LSTVFile << "AtLocTime = " + LSTVIt->AtLocTime + '\n';
16907  LSTVFile << "ArrTime = " + LSTVIt->ArrTime + '\n';
16908  LSTVFile << "DepTime = " + LSTVIt->DepTime + '\n';
16909  if(LSTVIt->FrhMarker == "")
16910  {
16911  LSTVFile << "Not Frh\n";
16912  }
16913  else
16914  {
16915  LSTVFile << LSTVIt->FrhMarker + '\n';
16916  }
16917  LSTVFile << '\n';
16918  }
16919  LSTVFile.close();
16920 */
16921  //declare pointers for use in printouts
16922  TLocServiceTimesVector::iterator Ptr1, Ptr2;
16923 
16924  //set up the output file
16925  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16926  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
16927 
16928  std::ofstream TTFile3(TTFileName3.c_str());
16929 
16930  if(TTFile3 == 0)
16931  {
16932  ShowMessage("Conflict Analysis file failed to open - can't be created");
16933  Utilities->CallLogPop(2210);
16934  return(false);
16935  }
16936  if(LocServiceTimesVector.empty())
16937  {
16938  ShowMessage("No timetabled services found");
16939  TTFile3.close();
16940  DeleteFile(TTFileName3);
16941  Utilities->CallLogPop(2211);
16942  return(false);
16943  }
16944  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n";
16945  TTFile3 << "See user manual or on-screen help section 5.12 for detailed information.\n\n\n";
16946  SequenceLog += "6\n";
16947 
16948 /* print out TrainDataVectorCopy for debugging purposes
16949 std::ofstream TDVCFile("TDVCFile.txt");
16950 for(TTrainDataVector::iterator TDVCIt = TrainDataVectorCopy.begin(); TDVCIt != TrainDataVectorCopy.end(); TDVCIt++)
16951 {
16952  TDVCFile << TDVCIt->ServiceReference + '\n';
16953  TDVCFile << TDVCIt->Description + '\n';
16954  for(unsigned int x = 0; x < TDVCIt->ActionVector.size(); x++)
16955  {
16956  TActionVectorEntry AVE = TDVCIt->ActionVector.at(x);
16957  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
16958  {
16959  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
16960  }
16961  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
16962  {
16963  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << " Arr " << AVE.LocationName << '\n';
16964  }
16965  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
16966  {
16967  TDVCFile << AnsiString(AVE.DepartureTime.TimeString()) << " Dep " << AVE.LocationName << '\n';
16968  }
16969  else if(AVE.FormatType == TimeTimeLoc)
16970  {
16971  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << ' ' << AnsiString(AVE.DepartureTime.TimeString()) << ' ' << AVE.LocationName << '\n';
16972  }
16973  else if(AVE.FormatType == PassTime)
16974  {
16975  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
16976  }
16977  else if(AVE.FormatType == ExitRailway)
16978  {
16979  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << " Fer" << '\n';
16980  }
16981  else if(AVE.FormatType == FinRemHere)
16982  {
16983  TDVCFile << "Frh" << '\n';
16984  }
16985  }
16986  TDVCFile << '\n';
16987 }
16988 TDVCFile.close();
16989 
16990 */
16991  //arrivals
16992  if(ArrChecked)
16993  {
16994  //sort in ArrTime order for each location
16995  Ptr1 = LocServiceTimesVector.begin();
16996  Ptr2 = Ptr1 + 1;
16997  while(Ptr2 != LocServiceTimesVector.end())
16998  {
16999  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17000  {
17001  Ptr2++;
17002  if(Ptr2 == LocServiceTimesVector.end())
17003  {
17004  break;
17005  }
17006  }
17007  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
17008  Ptr1 = Ptr2; //first entry with next name
17009  if(Ptr2 != LocServiceTimesVector.end())
17010  {
17011  Ptr2++;
17012  }
17013  }
17014 
17015  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
17016 
17017  TTFile3 << "Arrival & pass analysis: an asterisk means that the number of same approach code arrivals and passes is equal to or greater than the number of platforms.\n";
17018  TTFile3 << "If the total number of arrivals and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
17019  MinuteString = " minutes";
17020  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17021  if(ArrRange == 1)
17022  {
17023  MinuteString = " minute";
17024  }
17025  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
17026  TTFile3 << ",Platforms,Trains\n\n";
17027 
17028  Ptr1 = LocServiceTimesVector.begin();
17029  Ptr2 = Ptr1 + 1;
17030  while(Ptr2 != LocServiceTimesVector.end())
17031  {
17032  PreviousService = "";
17033  NumTrainsAtLoc = 0;
17034  ServiceAndRepeatNumTotal = "";
17035  NumPlats = 0;
17036  NumPlatsAtThisLocCalculated = false;
17037  BasicTime = "";
17038  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17039  {
17040  PreviousService = "";
17041  NumTrainsAtLoc = 0;
17042  ServiceAndRepeatNumTotal = "";
17043  NumPlats = 0;
17044  NumPlatsAtThisLocCalculated = false;
17045  BasicTime = "";
17046  Ptr1++;
17047  Ptr2++;
17048  if(Ptr2 == LocServiceTimesVector.end())
17049  {
17050  break;
17051  }
17052  }
17053  if(Ptr2 == LocServiceTimesVector.end())
17054  {
17055  break;
17056  }
17057  while(Ptr2->Location == Ptr1->Location)
17058  {
17059  PreviousService = "";
17060  NumTrainsAtLoc = 0;
17061  ServiceAndRepeatNumTotal = "";
17062  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
17063  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17064  {
17065  break;
17066  }
17067  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
17068  {
17069  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
17070  Ptr1++;
17071  Ptr2++;
17072  if(Ptr2 == LocServiceTimesVector.end())
17073  {
17074  break;
17075  }
17076  if(Ptr2->Location != Ptr1->Location)
17077  {
17078  break;
17079  }
17080  }
17081  if(Ptr2 == LocServiceTimesVector.end())
17082  {
17083  break;
17084  }
17085  if(Ptr2->Location != Ptr1->Location)
17086  {
17087  break;
17088  }
17089  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
17090  {
17091  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
17092  {
17093  break;
17094  }
17095  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17096  {
17097  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
17098  NumPlatsAtThisLocCalculated = true;
17099  }
17100  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17101  {
17102  if(ServiceAndRepeatNumTotal == "")
17103  {
17104  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
17105  NumTrainsAtLoc = 1;
17106  }
17107  else
17108  {
17109  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
17110  }
17111  }
17112  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
17113  if(ServiceAndRepeatNumTotal == "")
17114  {
17115  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
17116  NumTrainsAtLoc = 1;
17117  }
17118  else
17119  {
17120  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
17121  }
17122  Ptr1 = Ptr2;
17123  Ptr2++;
17124  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
17125  {
17126  int MaxNumberOfSameDirections = 0;
17127  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
17128  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
17129  {
17130 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
17131  TTFile3.close();
17132  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
17133 // Utilities->CallLogPop(2224);
17134 // return false;
17135  }
17136  AnsiString Asterisk = "";
17137  if(MaxNumberOfSameDirections >= NumPlats)
17138  {
17139  Asterisk = "* ";
17140  }
17141  //print out a single line for number of trains at loc with all service refs
17142  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
17143  ArrivalsPrinted = true;
17144  ServiceAndRepeatNumTotal = "";
17145  }
17146  if(Ptr2 == LocServiceTimesVector.end())
17147  {
17148  break;
17149  }
17150  if(Ptr2->Location != Ptr1->Location)
17151  {
17152  break;
17153  }
17154  }
17155  if(Ptr2 == LocServiceTimesVector.end())
17156  {
17157  break;
17158  }
17159  }
17160  }
17161  if(!ArrivalsPrinted)
17162  {
17163  TTFile3 << "Nothing to report for arrivals";
17164  }
17165  TTFile3 << "\n\n";
17166  }
17167  //end of routine for arrivals
17168  SequenceLog += "7\n";
17169  //departures
17170  if(DepChecked)
17171  {
17172  //sort in DepTime order for each location
17173  Ptr1 = LocServiceTimesVector.begin();
17174  Ptr2 = Ptr1 + 1;
17175  while(Ptr2 != LocServiceTimesVector.end())
17176  {
17177  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17178  {
17179  Ptr2++;
17180  if(Ptr2 == LocServiceTimesVector.end())
17181  {
17182  break;
17183  }
17184  }
17185  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
17186  Ptr1 = Ptr2; //first entry with next name
17187  if(Ptr2 != LocServiceTimesVector.end())
17188  {
17189  Ptr2++;
17190  }
17191  }
17192 
17193  //routine for departures - number of trains departing within the specified range with services listed at the end
17194  TTFile3 << "Departure & pass analysis: an asterisk means that the number of same exit code departures and passes is equal to or greater than the number of platforms.\n";
17195  TTFile3 << "If the total number of departures and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
17196  MinuteString = " minutes";
17197  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17198  if(DepRange == 1)
17199  {
17200  MinuteString = " minute";
17201  }
17202  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
17203  TTFile3 << ",Platforms,Trains\n\n";
17204 
17205  Ptr1 = LocServiceTimesVector.begin();
17206  Ptr2 = Ptr1 + 1;
17207  while(Ptr2 != LocServiceTimesVector.end())
17208  {
17209  PreviousService = "";
17210  NumTrainsAtLoc = 0;
17211  ServiceAndRepeatNumTotal = "";
17212  NumPlats = 0;
17213  NumPlatsAtThisLocCalculated = false;
17214  BasicTime = "";
17215  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17216  {
17217  PreviousService = "";
17218  NumTrainsAtLoc = 0;
17219  ServiceAndRepeatNumTotal = "";
17220  NumPlats = 0;
17221  NumPlatsAtThisLocCalculated = false;
17222  BasicTime = "";
17223  Ptr1++;
17224  Ptr2++;
17225  if(Ptr2 == LocServiceTimesVector.end())
17226  {
17227  break;
17228  }
17229  }
17230  if(Ptr2 == LocServiceTimesVector.end())
17231  {
17232  break;
17233  }
17234  while(Ptr2->Location == Ptr1->Location)
17235  {
17236  PreviousService = "";
17237  NumTrainsAtLoc = 0;
17238  ServiceAndRepeatNumTotal = "";
17239  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
17240  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17241  {
17242  break;
17243  }
17244  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
17245  {
17246  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
17247  Ptr1++;
17248  Ptr2++;
17249  if(Ptr2 == LocServiceTimesVector.end())
17250  {
17251  break;
17252  }
17253  if(Ptr2->Location != Ptr1->Location)
17254  {
17255  break;
17256  }
17257  }
17258  if(Ptr2 == LocServiceTimesVector.end())
17259  {
17260  break;
17261  }
17262  if(Ptr2->Location != Ptr1->Location)
17263  {
17264  break;
17265  }
17266  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
17267  {
17268  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
17269  {
17270  break;
17271  }
17272  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17273  {
17274  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
17275  NumPlatsAtThisLocCalculated = true;
17276  }
17277  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17278  {
17279  if(ServiceAndRepeatNumTotal == "")
17280  {
17281  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
17282  NumTrainsAtLoc = 1;
17283  }
17284  else
17285  {
17286  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
17287  }
17288  }
17289  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
17290  if(ServiceAndRepeatNumTotal == "")
17291  {
17292  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
17293  NumTrainsAtLoc = 1;
17294  }
17295  else
17296  {
17297  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
17298  }
17299  Ptr1 = Ptr2;
17300  Ptr2++;
17301  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
17302  {
17303  int MaxNumberOfSameDirections = 0;
17304  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
17305  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
17306  {
17307 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
17308  TTFile3.close();
17309  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
17310 // Utilities->CallLogPop(2225);
17311 // return false;
17312  }
17313  AnsiString Asterisk = "";
17314  if(MaxNumberOfSameDirections >= NumPlats)
17315  {
17316  Asterisk = "* ";
17317  }
17318  //print out a single line for number of trains at loc with all service refs
17319  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
17320  DeparturesPrinted = true;
17321  ServiceAndRepeatNumTotal = "";
17322  }
17323  if(Ptr2 == LocServiceTimesVector.end())
17324  {
17325  break;
17326  }
17327  if(Ptr2->Location != Ptr1->Location)
17328  {
17329  break;
17330  }
17331  }
17332  if(Ptr2 == LocServiceTimesVector.end())
17333  {
17334  break;
17335  }
17336  }
17337  }
17338  if(!DeparturesPrinted)
17339  {
17340  TTFile3 << "Nothing to report for departures";
17341  }
17342  TTFile3 << "\n\n";
17343  }
17344  //end of routine for departures
17345  SequenceLog += "8\n";
17346 
17347  //list trains at locations at same time
17348 
17349  if(AtLocChecked)
17350  {
17351  //sort in AtLocTime order for each location
17352  Ptr1 = LocServiceTimesVector.begin();
17353  Ptr2 = Ptr1 + 1;
17354  while(Ptr2 != LocServiceTimesVector.end())
17355  {
17356  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17357  {
17358  Ptr2++;
17359  if(Ptr2 == LocServiceTimesVector.end())
17360  {
17361  break;
17362  }
17363  }
17364  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
17365  Ptr1 = Ptr2; //first entry with next name
17366  if(Ptr2 != LocServiceTimesVector.end())
17367  {
17368  Ptr2++;
17369  }
17370  }
17371 
17372  //print out simultaneous AtLocs (don't need range of times for AtLocs)
17373  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
17374  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
17375  TTFile3 << ",Platforms,Trains,\n\n";
17376  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17377  Ptr1 = LocServiceTimesVector.begin();
17378  Ptr2 = Ptr1 + 1;
17379  while(Ptr2 != LocServiceTimesVector.end())
17380  {
17381  PreviousService = "";
17382  ServiceAndRepeatNumTotal = "";
17383  NumTrainsAtLoc = 0;
17384  NumPlats = 0;
17385  NumPlatsAtThisLocCalculated = false;
17386  FrhCount = 0;
17387  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17388  {
17389  PreviousService = "";
17390  ServiceAndRepeatNumTotal = "";
17391  NumTrainsAtLoc = 0;
17392  NumPlats = 0;
17393  NumPlatsAtThisLocCalculated = false;
17394  FrhCount = 0;
17395  Ptr1++;
17396  Ptr2++;
17397  if(Ptr2 == LocServiceTimesVector.end())
17398  {
17399  break;
17400  }
17401  }
17402  if(Ptr2 == LocServiceTimesVector.end())
17403  {
17404  break;
17405  }
17406  while(Ptr2->Location == Ptr1->Location)
17407  {
17408  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
17409  {
17410  FrhCount++;
17411  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17412  }
17413  PreviousService = "";
17414  NumTrainsAtLoc = 0;
17415  ServiceAndRepeatNumTotal = "";
17416  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17417  {
17418  break;
17419  }
17420  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
17421  {
17422  Ptr1++;
17423  if(Ptr1->FrhMarker == "Frh")
17424  {
17425  FrhCount++;
17426  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17427  }
17428  Ptr2++;
17429  if(Ptr2 == LocServiceTimesVector.end())
17430  {
17431  break;
17432  }
17433  if(Ptr2->Location != Ptr1->Location)
17434  {
17435  break;
17436  }
17437  }
17438  if(Ptr2 == LocServiceTimesVector.end())
17439  {
17440  break;
17441  }
17442  if(Ptr2->Location != Ptr1->Location)
17443  {
17444  break;
17445  }
17446  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
17447  {
17448  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
17449  {
17450  break;
17451  }
17452  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17453  {
17454  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
17455  NumPlatsAtThisLocCalculated = true;
17456  }
17457  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17458  {
17459  if(ServiceAndRepeatNumTotal == "")
17460  {
17461  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
17462  NumTrainsAtLoc = 1;
17463  }
17464  else
17465  {
17466  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
17467  }
17468  }
17469  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
17470  if(ServiceAndRepeatNumTotal == "")
17471  {
17472  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
17473  NumTrainsAtLoc = 1;
17474  }
17475  else
17476  {
17477  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
17478  }
17479  Ptr1 = Ptr2;
17480  if(Ptr1->FrhMarker == "Frh")
17481  {
17482  FrhCount++;
17483  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17484  }
17485  Ptr2++;
17486  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
17487  {
17488 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
17489 //new text //don't print out if all remainers or if only 1 train at loc
17490  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
17491 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
17492 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
17493  {
17494  AnsiString Asterisk = "";
17495  if(NumTrainsAtLoc > NumPlats)
17496  {
17497  Asterisk = "* ";
17498  }
17499  //print out a single line for number of trains at loc with all service refs
17500  if(FrhCount == 0)
17501  {
17502  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
17503  }
17504  else if(FrhCount == 1)
17505  {
17506  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
17507  }
17508  else
17509  {
17510  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
17511  }
17512  LastFrhCount = FrhCount;
17513  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
17514  AtLocsPrinted = true;
17515  ServiceAndRepeatNumTotal = "";
17516  }
17517  }
17518  if(Ptr2 == LocServiceTimesVector.end())
17519  {
17520  break;
17521  }
17522  if(Ptr2->Location != Ptr1->Location)
17523  {
17524  break;
17525  }
17526  }
17527  if(Ptr2 == LocServiceTimesVector.end())
17528  {
17529  break;
17530  }
17531  }
17532  }
17533  if(!AtLocsPrinted)
17534  {
17535  TTFile3 << "Nothing to report for trains at locations";
17536  }
17537  TTFile3 << "\n\n";
17538  //end of simultaneous AtLocs
17539  }
17540  SequenceLog += "9\n";
17541 /*
17542  //print out the full vector here for testing purposes
17543  TTFile3 << "Full LocServiceTimesVector\n\n";
17544  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
17545 
17546  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
17547  {
17548  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
17549  }
17550 
17551  TTFile3 << "\n\n\n";
17552 */
17553 /*cdt analysis - added at v2.10.0
17554 2 pass system: 1st extract as a single service all Snt (or Snt-sh) starts, with Fns/Sns links combined (and F-nshs/Sns-sh) (though add a new
17555 changeover code [chr XXXX - 'change ref + new reference] until come to Fjo, Frh, Frh-sh, Fer (ignore exit loc as can't stop there), ignore jbos &
17556 repeats, but with fsp & rsp store all the foregoing service entries along with the split reference & add that to the relevant Sfs entry as a new
17557 service. For shuttles with feeder start with feeder & progress into shuttle, ending when finish & remain here or progressing into the finishing
17558 service.
17559 
17560 Use The TrainDataVectorCopy as that has all unique service refs.
17561 
17562 2nd run the cdt checker similar to that in SecondPassActions, but where a same name found either side of a changeover code quote both refs. Add a
17563 similar unexpected cdt check where if have different locs either side of a cdt then may be inappropriate.
17564 
17565 First create a new TrainDataVector from earlier copy as above with single services
17566 */
17567  if(DirChecked)
17568  {
17569  //direction analysis added at v2.10.0
17570  TTrainDataEntry SingleServiceEntry, PartServiceEntry, NewPartServiceEntry, TempEntry;
17571  TTrainDataVector SingleServiceVector, PartServiceVector;
17572 
17573  //find new train services (Snt or Snt-sh) & remember that entries can be in any order
17574  //NB: ALWAYS use OtherHeadCode (which is now a service reference) for any follow-on service
17575  TTFile3 << "Train direction analysis - consisting of train facing directions on creation and possible missing or questionable changes of direction:\n\n";
17576  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
17577  {
17578  TTrainDataEntry TDE = TrainDataVectorCopy.at(x);
17579  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
17580  {
17581  TDE.ActionVector.erase(&TDE.ActionVector.back()); //strip repeat entry if present
17582  }
17583  const TActionVector &AV = TDE.ActionVector;
17584  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
17585  {
17586  SingleServiceEntry = TDE;
17587  TActionVector &SSAV = SingleServiceEntry.ActionVector;
17588  for(unsigned int y = 0; y < SSAV.size(); y++)
17589  {
17590  if((SSAV.at(y).Command == "Fjo") || (SSAV.at(y).Command == "Frh") || (SSAV.at(y).Command == "Fer") || (SSAV.at(y).Command == "Frh-sh"))
17591  {
17592  SingleServiceVector.push_back(SingleServiceEntry); //push the complete entry
17593  break; //finished with this one
17594  }
17595  else if((SSAV.at(y).Command == "fsp") || (SSAV.at(y).Command == "rsp"))
17596  {
17597  PartServiceEntry = TDE; //start with complete entry
17598  PartServiceEntry.ActionVector.clear(); //clear AV
17599  for(unsigned int z = 0; z <= y; z++)
17600  {
17601  PartServiceEntry.ActionVector.push_back(TDE.ActionVector.at(z)); //add back all AVs up to & inc fsp/rsp
17602  if(z == y)
17603  {
17604  PartServiceEntry.ActionVector.at(z).Command = "chr-sp"; //change split command to chr
17605  PartServiceEntry.ActionVector.at(z).OtherHeadCode = PartServiceEntry.ActionVector.at(z).LinkedTrainEntryPtr->ServiceReference;
17606  }
17607  }
17608  PartServiceVector.push_back(PartServiceEntry);
17609  if(SSAV.at(y).Command == "fsp")
17610  {
17611  SSAV.at(y).Command = "Front split - original service continues below";
17612  SSAV.at(y).OtherHeadCode = "";
17613  }
17614  if(SSAV.at(y).Command == "rsp")
17615  {
17616  SSAV.at(y).Command = "Rear split - original service continues below";
17617  SSAV.at(y).OtherHeadCode = "";
17618  }
17619  //don't break & continue here because the original train carries on
17620  }
17621  else if(SSAV.at(y).Command == "Fns")
17622  {
17623  SSAV.at(y).Command = "chr-Fns";
17624  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
17625  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17626  break; //from y loop
17627  }
17628  else if(SSAV.at(y).Command == "Fns-sh")
17629  {
17630  SSAV.at(y).Command = "chr-Fns-sh";
17631  SSAV.at(y).OtherHeadCode = SSAV.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
17632  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
17633  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17634  break; //from y loop
17635  }
17636  else if(SSAV.at(y).Command == "F-nshs")
17637  {
17638  SSAV.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
17639  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
17640  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
17641  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17642  break; //from y loop
17643  }
17644  }
17645  }
17646  }
17647  SequenceLog += "10\n";
17648  //now have all complete entries in SingleServiceVector and all part services in PartServiceVector but without any follow-ons
17649 
17650  //Now add Sns & Sns-sh services to PartServiceVector entries
17651  AnsiString NextRef;
17652  while(!PartServiceVector.empty())
17653  {
17654  PartServiceEntry = PartServiceVector.at(0); //deal with front entry and add new entries at the back
17655  for(unsigned int y = 0; y < PartServiceEntry.ActionVector.size(); y++)
17656  {
17657  if(PartServiceEntry.ActionVector.at(y).Command.SubString(1,3) == "chr")
17658  {
17659  NextRef = PartServiceEntry.ActionVector.at(y).OtherHeadCode;
17660  }
17661  }
17662  //find it in TrainDataVectorCopy
17663  bool FinishType = true, FoundFlag = false;
17664  while(FinishType)
17665  {
17666  TempEntry = GetServiceFromVector(0, NextRef, TrainDataVectorCopy, FinishType, FoundFlag); //FinishType is a bool where false = Final (Fjo, Frh, Fer, or
17667  //Frh-sh); true = MoreToCome (Fns, Fns-sh, F-nshs)
17668  if(FoundFlag)
17669  {
17670  for(unsigned int y = 1; y < TempEntry.ActionVector.size(); y++) //starts at 1 as that is the entry after the start entry
17671  {
17672  if((TempEntry.ActionVector.at(y).Command == "") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
17673  {
17674  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17675  }
17676  else if((TempEntry.ActionVector.at(y).Command[1] != 'F') && (TempEntry.ActionVector.at(y).Command != "fsp") && (TempEntry.ActionVector.at(y).Command != "rsp") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
17677  {
17678  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17679  }
17680  else
17681  {
17682  if((TempEntry.ActionVector.at(y).Command == "Fjo") || (TempEntry.ActionVector.at(y).Command == "Frh") || (TempEntry.ActionVector.at(y).Command == "Fer") || (TempEntry.ActionVector.at(y).Command == "Frh-sh"))
17683  {
17684  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17685  SingleServiceVector.push_back(PartServiceVector.at(0)); //push the complete entry
17686  PartServiceVector.erase(&PartServiceVector.at(0));
17687  break; //from y loop
17688  }
17689  else if((TempEntry.ActionVector.at(y).Command == "fsp") || (TempEntry.ActionVector.at(y).Command == "rsp"))
17690  {
17691  NewPartServiceEntry = PartServiceVector.at(0); //covers everything up to but excluding the split
17692  NewPartServiceEntry.ActionVector.push_back(TempEntry.ActionVector.at(y)); //now includes the split
17693  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).Command = "chr-sp"; //change split command to chr
17694  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).OtherHeadCode = NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).LinkedTrainEntryPtr->ServiceReference;
17695  PartServiceVector.push_back(NewPartServiceEntry); //new entry for the split service
17696  if(TempEntry.ActionVector.at(y).Command == "fsp")
17697  {
17698  TempEntry.ActionVector.at(y).Command = "Front split - original service continues below";
17699  TempEntry.ActionVector.at(y).OtherHeadCode = "";
17700  }
17701  if(TempEntry.ActionVector.at(y).Command == "rsp")
17702  {
17703  TempEntry.ActionVector.at(y).Command = "Rear split - original service continues below";
17704  TempEntry.ActionVector.at(y).OtherHeadCode = "";
17705  }
17706  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17707  }
17708  else if(TempEntry.ActionVector.at(y).Command == "Fns")
17709  {
17710  TempEntry.ActionVector.at(y).Command = "chr-Fns";
17711  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
17712  TempEntry.ActionVector.at(y).OtherHeadCode = NextRef;
17713  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
17714  break; //from y loop
17715  }
17716  else if(TempEntry.ActionVector.at(y).Command == "Fns-sh")
17717  {
17718  TempEntry.ActionVector.at(y).Command = "chr-Fns-sh";
17719  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
17720  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
17721  NextRef = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
17722  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
17723  break; //from y loop
17724  }
17725  else if(TempEntry.ActionVector.at(y).Command == "F-nshs")
17726  {
17727  TempEntry.ActionVector.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
17728  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
17729  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
17730  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
17731  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
17732  break; //from y loop
17733  }
17734  }
17735  }
17736  }
17737  else
17738  {
17739  SequenceLog += + "11\n";
17740  throw Exception("Unable to find service reference " + NextRef + " Last ref checked = " + TempEntry.ServiceReference);
17741  }
17742  }
17743  }
17744  if(!PartServiceVector.empty())
17745  {
17746  SequenceLog += "12\n";
17747  throw Exception("PartServiceVector should be empty here - size = " + PartServiceVector.size());
17748  }
17749  SequenceLog += "13\n";
17750  /*
17751  form:-
17752  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
17753  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
17754  then multiple entries, separated by commas, of the form:-
17755 
17756  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
17757  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
17758  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
17759 
17760  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
17761  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
17762  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
17763 
17764  HH:MM;Command (cdt) }TimeCmd }
17765  HH:MM;Location (arr & dep) }TimeLoc }
17766  HH:MM;HH:MM;Location }TimeTimeLoc }
17767  HH:MM;pas;Location }PassTime }
17768  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
17769  HH:MM;Fer;set of allowable IDs }ExitRailway }
17770  Command (Frh only) }FinRemHere }
17771 
17772  R;mm;dd;nn. Repeat Repeat entry
17773 
17774  Formats:
17775 
17776  Command only: Frh
17777  Time;Command: cdt
17778  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
17779  Time;Command;2 Element IDs: Snt
17780  Time;Comand;n Element IDs: Fer
17781  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
17782  Time;Command;2 Element IDs;Headcode Snt-sh
17783  Time;Command;Location pas
17784  Time;Location Arr Dep
17785  Time;Time;Location Arr & dep together
17786  */
17787 
17788 /*
17789 Perform the starting direction check (Snt & Snt-sh entries). Starts with the train's front element,
17790 checking forwards until it comes to a continuation (no report), a location name that is not null and
17791 different to the train's front element name (whether null or not) (no report), a leading point
17792 (no report) or buffers (report).
17793 */
17794  bool BufferFacingUnReportedFlag = true;
17795  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
17796  {
17797  TTrackElement ThisElement, NextElement;
17798  TTrainDataEntry TDE = SingleServiceVector.at(x);
17799  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
17800  {
17801  SequenceLog += "13a\n";
17802  throw Exception("Repeat entry present in SingleServiceVector at position " + x);
17803  }
17804  const TActionVector &AV = TDE.ActionVector;
17805  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
17806  {
17807  bool BufferFlag = false;
17808  int FrontTVPos = AV.at(0).FrontStartOrRepeatDigits;
17809  int RearTVPos = AV.at(0).RearStartOrRepeatMins;
17810  AnsiString FrontLocName = AV.at(0).LocationName;
17811  int NextEntryPos, NextExitPos;
17812  ThisElement = Track->TrackElementAt(1395, FrontTVPos);
17813  int ThisExitPos;
17814  if(ThisElement.Conn[0] == RearTVPos)
17815  {
17816  ThisExitPos = 1;
17817  }
17818  else if(ThisElement.Conn[1] == RearTVPos)
17819  {
17820  ThisExitPos = 0;
17821  }
17822  else if(ThisElement.Conn[2] == RearTVPos)
17823  {
17824  ThisExitPos = 3;
17825  }
17826  else if(ThisElement.Conn[3] == RearTVPos)
17827  {
17828  ThisExitPos = 2;
17829  }
17830  if((ThisElement.TrackType == Buffers) && (ThisExitPos == 0))//pos 0 is the buffer
17831  {
17832  BufferFlag = true;
17833  }
17834  else //continue tracking forwards
17835  {
17836  while(true)
17837  {
17838  if(ThisElement.Conn[ThisExitPos] == -1)
17839  {
17840  SequenceLog = "13b\n";
17841  throw Exception("ThisElement connects to -1 for " + TDE.ServiceReference);
17842  }
17843  NextElement = Track->TrackElementAt(1396, ThisElement.Conn[ThisExitPos]);
17844  NextEntryPos = ThisElement.ConnLinkPos[ThisExitPos];
17845  if((NextElement.TrackType == Points) && ((NextEntryPos == 0) || (NextEntryPos == 2))) //leading points
17846  {
17847  BufferFlag = false; //should already be false
17848  break;
17849  }
17850  else if(NextElement.TrackType == Continuation)
17851  {
17852  BufferFlag = false;
17853  break;
17854  }
17855  else if((NextElement.ActiveTrackElementName != "") && (NextElement.ActiveTrackElementName != FrontLocName))
17856  {
17857  BufferFlag = false;
17858  break;
17859  }
17860  else if(NextElement.TrackType == Buffers)
17861  {
17862  BufferFlag = true;
17863  break;
17864  }
17865  else if((NextElement.TrackType == Points) && ((NextEntryPos == 1) || (NextEntryPos == 3))) //trailing points
17866  {
17867  ThisElement = NextElement;
17868  ThisExitPos = 0;
17869  continue;
17870  }
17871  else
17872  {
17873  if(NextEntryPos == 0)
17874  {
17875  NextExitPos = 1;
17876  }
17877  else if(NextEntryPos == 1)
17878  {
17879  NextExitPos = 0;
17880  }
17881  else if(NextEntryPos == 2)
17882  {
17883  NextExitPos = 3;
17884  }
17885  else if(NextEntryPos == 3)
17886  {
17887  NextExitPos = 2;
17888  }
17889  }
17890  ThisElement = NextElement;
17891  ThisExitPos = NextExitPos;
17892  }
17893  }
17894  if(BufferFlag)
17895  {
17896  if(BufferFacingUnReportedFlag)
17897  {
17898  TTFile3 << "Train facing direction on creation analysis:-\n\n";
17899  BufferFacingUnReportedFlag = false;
17900  }
17901  TTFile3 << "Service " + TDE.ServiceReference + " facing buffers on creation\n";
17902  }
17903  }
17904  }
17905  if(BufferFacingUnReportedFlag)
17906  {
17907  TTFile3 << "Nothing to report for train facing directions\n\n";
17908  }
17909  else
17910  {
17911  TTFile3 << '\n';
17912  }
17913  SequenceLog += "13c\n";
17914 
17915  //Perform the missing cdt check. Check every entry simiar to the check in SecondPassActions and if find any print out the full sequence and service entries
17916  AnsiString LocationNameToBeChecked = "";
17917  bool MissingcdtUnreportedFlag = true;
17918  TNumList MarkerList;
17919  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
17920  {
17921  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
17922  unsigned int y = 0;
17923  int FirstInstance = 9999, SecondInstance = 9999; //9999 ensures wont be marked if not changed
17924  bool FullBreak = false;
17925  MarkerList.clear();
17926  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
17927  // first discard unlocated Snt entries as they don't have location name set
17928  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
17929  {
17930  y = 1;
17931  }
17932  while((y < TDEntry.ActionVector.size()) && !FullBreak)
17933  // need to check each location name separately in turn, skipped for SignallerControl entries
17934  {
17935  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat) ||
17936  (TDEntry.ActionVector.at(y).Command == "Fjo") || (TDEntry.ActionVector.at(y).Command == "Frh") ||
17937  (TDEntry.ActionVector.at(y).Command == "Frh-sh"))
17938  {
17939  break; // out of the 'while' loop since have reached the end
17940  }
17941  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
17942  FirstInstance = y;
17943  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
17944  {
17945  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
17946  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) ||
17947  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
17948  (AVEntry.Command == "Frh-sh"))
17949  {
17950  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
17951  }
17952  if(AVEntry.Command == "cdt")
17953  {
17954  break; // out of the 'z' loop since the check is only valid up to a change of direction
17955  }
17956  if(AVEntry.LocationName == LocationNameToBeChecked)
17957  {
17958  continue; // keep going while name same
17959  }
17960  if(AVEntry.LocationName != LocationNameToBeChecked)
17961  // if name different check forwards to see if repeats
17962  {
17963  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
17964  {
17965  if(TDEntry.ActionVector.at(a).Command == "cdt")
17966  {
17967  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
17968  }
17969  if(TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked)
17970  {
17971  SecondInstance = a;
17972  AnsiString Sequence = TDEntry.ServiceReference;
17973  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
17974  {
17975  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
17976  {
17977  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
17978  }
17979  }
17980  if(MissingcdtUnreportedFlag)
17981  {
17982  TTFile3 << "Possibly missing changes of direction - these will be missing unless the service travels in a loop back to the locations marked:-\n\n";
17983  }
17984  TTFile3 << LocationNameToBeChecked << " is listed twice with no direction change between in service sequence: " << Sequence << "\n\n";
17985  MarkerList.push_back(FirstInstance);
17986  MarkerList.push_back(SecondInstance);
17987  SingleServiceOutput(0, x, MarkerList, SingleServiceVector, TTFile3);
17988  MissingcdtUnreportedFlag = false;
17989  FullBreak = true; //no more checks for this sequence
17990  break; //out of the a & z loops
17991  }
17992  }
17993  break; // out of the 'z' loop since have checked 'a' as far as need to
17994  }
17995  }
17996  y++;
17997  }
17998  }
17999  if(MissingcdtUnreportedFlag)
18000  {
18001  TTFile3 << "Nothing to report for missing changes of direction\n\n";
18002  }
18003  else
18004  {
18005  TTFile3 << '\n';
18006  }
18007  SequenceLog += "14\n";
18008 
18009 /* Perform the questionable cdt check. Examine each service in the SingleServiceVector, and if don't find the same
18010  name either side of a cdt (before the next cdt) then flag as a questionable cdt.
18011  Method: Have an outer loop for each service that looks for cdts. When found work backwards to the last cdt or beginning and std::list all the
18012  locations excluding the cdt location. Then work forwards to the next cdt or the end and do the same. Sort each list and make unique (duplicated
18013  names on one side of a cdt already checked either by the tt validator or the missing cdt check. Then compare the two lists and if any location
18014  included in both then ok, else report as questionable. If one list is empty then it is reported.
18015 */
18016  typedef std::list<AnsiString> TLocList;
18017  TLocList BackwardList, ForwardList;
18018  bool IntroLineNeeded = true;
18019  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18020  {
18021  unsigned int cdtPosition = 9999;
18022  AnsiString cdtLocation = "";
18023  bool FoundSameName = false;
18024  MarkerList.clear();
18025  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
18026  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
18027  // need to check each location name separately in turn, skipped for SignallerControl entries
18028  {
18029  BackwardList.clear();
18030  ForwardList.clear();
18031  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
18032  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) || //shouldn't be any repeats
18033  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
18034  (AVEntry.Command == "Frh-sh"))
18035  {
18036  if(MarkerList.empty())
18037  {
18038  break; // out of the 'y' loop since have reached the end & nothing to report
18039  }
18040  else
18041  {
18042  AnsiString Sequence = TDEntry.ServiceReference;
18043  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
18044  {
18045  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
18046  {
18047  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
18048  }
18049  }
18050  MarkerList.sort();
18051  if(IntroLineNeeded)
18052  {
18053  TTFile3 << "Questionable change of direction analysis.\n\n";
18054  TTFile3 << "For marked changes of direction there are no same-name locations listed both above and below.\n";
18055  TTFile3 << "These changes of direction are probably valid for movements to and from depots but all should be checked to\n";
18056  TTFile3 << "make sure that none has been included incorrectly:\n\n";
18057  IntroLineNeeded = false;
18058  }
18059  TTFile3 << "Service sequence " << Sequence << " contains questionable changes of direction:-\n\n";
18060  SingleServiceOutput(1, x, MarkerList, SingleServiceVector, TTFile3);
18061  break;
18062  }
18063  }
18064  if(AVEntry.Command != "cdt")
18065  {
18066  continue; //only looking for cdts
18067  }
18068  //here have found a cdt
18069  cdtPosition = y;
18070  cdtLocation = AVEntry.LocationName;
18071  for(int z = y - 1; z >= 0; z--)
18072  {
18073  const TActionVectorEntry &AVEntry2 = TDEntry.ActionVector.at(z);
18074  if(AVEntry2.Command == "cdt")
18075  {
18076  break; //don't look further back than the last cdt
18077  }
18078  if((AVEntry2.LocationName != "") && (AVEntry2.LocationName != cdtLocation)) //if an earlier entry == cdtLocation will have been picked up in an earlier check
18079  {
18080  BackwardList.push_back(AVEntry2.LocationName);
18081  }
18082  }
18083  BackwardList.sort();
18084  BackwardList.unique();
18085  for(unsigned int z = y + 1; z < TDEntry.ActionVector.size(); z++)
18086  {
18087  const TActionVectorEntry &AVEntry3 = TDEntry.ActionVector.at(z);
18088  if((AVEntry3.Command == "Fer") || (AVEntry3.FormatType == Repeat) ||
18089  (AVEntry3.Command == "Fjo") || (AVEntry3.Command == "Frh") ||
18090  (AVEntry3.Command == "Frh-sh") || (AVEntry3.Command == "cdt"))
18091  {
18092  break; // out of the 'z' loop since have reached another cdt or the end
18093  }
18094  if((AVEntry3.LocationName != "") && (AVEntry3.LocationName != cdtLocation)) //if a later entry == cdtLocation will have been picked up in an earlier check
18095  {
18096  ForwardList.push_back(AVEntry3.LocationName);
18097  }
18098  }
18099  ForwardList.sort();
18100  ForwardList.unique();
18101  FoundSameName = false;
18102  //now have both lists compiled (may be empty) so check for same name in both & report if don't find any
18103  if(!BackwardList.empty() && !ForwardList.empty())
18104  {
18105  for(TLocList::iterator BLIt = BackwardList.begin(); BLIt != BackwardList.end(); BLIt++)
18106  {
18107  for(TLocList::iterator FLIt = ForwardList.begin(); FLIt != ForwardList.end(); FLIt++)
18108  {
18109  if(*BLIt == *FLIt)
18110  {
18111  FoundSameName = true;
18112  }
18113  }
18114  }
18115  }
18116  if(!FoundSameName) //report the inability to find same name
18117  {
18118  MarkerList.push_back(cdtPosition);
18119  }
18120  }
18121  }
18122  if(IntroLineNeeded)
18123  {
18124  TTFile3 << "Nothing to report for questionable changes of direction\n\n";
18125  }
18126  else
18127  {
18128  TTFile3 << '\n';
18129  }
18130 /*
18131 //print all SSVector for diagnostic purposes
18132  TTFile3 << "Whole SSVector\n\n";
18133  TNumList EmptyList;
18134  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18135  {
18136  SingleServiceOutput(, x, EmptyList, SingleServiceVector, TTFile3);
18137  }
18138 */
18139  }
18140  SequenceLog += "15\n";
18141  TTFile3.close();
18142  Utilities->CallLogPop(2212);
18143  return(true);
18144  }
18145 
18146  catch(const Exception &e) //non error catch
18147  {
18148  AnsiString TTErrorFileName = "Analysis Error.txt";
18149  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
18150  std::ofstream TTError(TTErrorFileName.c_str());
18151  if(TTError == 0)
18152  {
18153  ShowMessage("Analysis error file failed to open - can't be created");
18154  Utilities->CallLogPop(2233);
18155  return(false);
18156  }
18157  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
18158  TTError << TimeNow.c_str() << '\n' << ArrRange << '\n' << ArrChecked << '\n' << DepRange << '\n' <<
18159  DepChecked << '\n' << AtLocChecked << '\n' << SequenceLog << '\n' << AnsiString(e.Message);
18160 
18161  TTError.close();
18162  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
18163  Utilities->CallLogPop(2226);
18164  return(false);
18165  }
18166 }
18167 
18168 // ---------------------------------------------------------------------------
18169 void TTrainController::SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
18170 {
18171  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(SSVectorNumber) + ',' + ",SingleServiceOutput");
18172  if((SSVectorNumber < 0) || ((unsigned int)SSVectorNumber >= SingleServiceVector.size()))
18173  {
18174  throw Exception("SSVectorNumber out of range, = " + AnsiString(SSVectorNumber) + ", Vector size = " + SingleServiceVector.size());
18175  }
18176  TTrainDataEntry SingleService = SingleServiceVector.at(SSVectorNumber);
18177  {
18178  VecFile << ",Initial service reference " << SingleService.ServiceReference + '\n';
18179  AnsiString Marker = "";
18180  for(unsigned int x = 0; x < SingleService.ActionVector.size(); x++)
18181  {
18182  Marker = ',';
18183  for(TNumListIterator MLIt = MarkerList.begin(); MLIt != MarkerList.end(); MLIt++)
18184  {
18185  if(int(x) == *MLIt)
18186  {
18187  Marker = "-->,";
18188  break;
18189  }
18190  }
18191  TActionVectorEntry AVE = SingleService.ActionVector.at(x);
18192  if(AVE.FormatType == StartNew)
18193  {
18194  AnsiString RearID = Track->TrackElementAt(1397, AVE.RearStartOrRepeatMins).ElementID;
18195  AnsiString FrontID = Track->TrackElementAt(1398, AVE.FrontStartOrRepeatDigits).ElementID;
18196  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << '\n';
18197  }
18198  if(AVE.FormatType == SNTShuttle)
18199  {
18200  AnsiString RearID = Track->TrackElementAt(1399, AVE.RearStartOrRepeatMins).ElementID;
18201  AnsiString FrontID = Track->TrackElementAt(1400, AVE.FrontStartOrRepeatDigits).ElementID;
18202  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << ' ' << AVE.OtherHeadCode << '\n';
18203  }
18204  if(AVE.FormatType == SNSShuttle) //should all have been converted to chr
18205  {
18206  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
18207  }
18208  if(AVE.FormatType == Repeat) //shouldn't be any repeats, only here to show if any have been copied
18209  {
18210  VecFile << Marker << "Repeat " << AVE.RearStartOrRepeatMins << ' ' << AVE.FrontStartOrRepeatDigits << ' ' << AVE.NumberOfRepeats << '\n';
18211  }
18212  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
18213  {
18214  TActionVectorEntry AVHolder = AVE;
18215  if(AVE.Command.SubString(1,3) == "chr")
18216  {
18217  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "sp")
18218  {
18219  AVE.Command = "Change of service to " + AVE.OtherHeadCode + " after split";
18220  AVE.OtherHeadCode = "";
18221  }
18222  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns")
18223  {
18224  AVE.Command = "Change of service to ";
18225  }
18226  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns-sh")
18227  {
18228  AVE.Command = "Change to shuttle finishing service";
18229  }
18230  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "F-nshs")
18231  {
18232  AVE.Command = "Change to shuttle service " + AVE.OtherHeadCode + " from feeder";
18233  AVE.OtherHeadCode = "";
18234  }
18235  }
18236  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << '\n';
18237  AVE = AVHolder;
18238  }
18239  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18240  {
18241  VecFile << Marker << AnsiString(AVE.ArrivalTime.TimeString()) << " Arr " << AVE.LocationName << '\n';
18242  }
18243  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
18244  {
18245  VecFile << Marker << AnsiString(AVE.DepartureTime.TimeString()) << " Dep " << AVE.LocationName << '\n';
18246  }
18247  else if(AVE.FormatType == TimeTimeLoc)
18248  {
18249  VecFile << Marker << AnsiString(AVE.ArrivalTime.TimeString()) << ' ' << AnsiString(AVE.DepartureTime.TimeString()) << ' ' << AVE.LocationName << '\n';
18250  }
18251  else if(AVE.FormatType == PassTime)
18252  {
18253  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
18254  }
18255  else if(AVE.FormatType == ExitRailway) //ListOfExits added at v2.10.0
18256  {
18257  AnsiString ListOfExits = "";
18258  for(TNumListIterator NLIt = AVE.ExitList.begin(); NLIt != AVE.ExitList.end(); NLIt++)
18259  {
18260  ListOfExits += AnsiString(Track->TrackElementAt(1432, *NLIt).ElementID) + ' ';
18261  }
18262  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << " Fer " << ListOfExits <<'\n';
18263  }
18264  else if(AVE.FormatType == FinRemHere)
18265  {
18266  VecFile << Marker << "Frh" << '\n';
18267  }
18268  }
18269  VecFile << '\n';
18270  }
18271  Utilities->CallLogPop(2318);
18272 }
18273 
18274 // ---------------------------------------------------------------------------
18275 
18276 TTrainDataEntry TTrainController::GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
18277 {
18278  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetServiceFromVector," + ServiceReference);
18279  FoundFlag = false;
18280  FinishType = true;
18281  for(unsigned int x = 0; x < Vector.size(); x++)
18282  {
18283  if(Vector.at(x).ServiceReference == ServiceReference)
18284  {
18285  if(Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1).FormatType == Repeat) //shouldn't be any repeats
18286  {
18287  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 2);
18288  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
18289  {
18290  FinishType = false;
18291  }
18292  }
18293  else
18294  {
18295  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1);
18296  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
18297  {
18298  FinishType = false;
18299  }
18300  }
18301  FoundFlag = true;
18302  Utilities->CallLogPop(2319);
18303  return(Vector.at(x));
18304  }
18305  }
18306  Utilities->CallLogPop(2320);
18307  return(Vector.at(Vector.size() - 1)); //return last for want of returning something
18308 }
18309 
18310 // ---------------------------------------------------------------------------
18311 
18312 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
18313 {
18314 //convert times to integer minutes
18315  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
18316  if((Time1 == "") || (Time2 == ""))
18317  {
18318  Utilities->CallLogPop(2213);
18319  return(false);
18320  }
18321  int Mins = Time1.SubString(4,2).ToInt();
18322  int Hours = Time1.SubString(1,2).ToInt();
18323  int Time1Mins = (Hours * 60) + Mins;
18324  Mins = Time2.SubString(4,2).ToInt();
18325  Hours = Time2.SubString(1,2).ToInt();
18326  int Time2Mins = (Hours * 60) + Mins;
18327  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
18328  {
18329  Utilities->CallLogPop(2214);
18330  return(true);
18331  }
18332  Utilities->CallLogPop(2215);
18333  return(false);
18334 }
18335 
18336 // ---------------------------------------------------------------------------
18337 
18338 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
18339  bool &AnalysisError, int &MaxNumberOfSameDirections)
18340 {
18341  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
18342 
18343  try
18344  {
18345  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
18346  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
18347  int SCPos = 0;
18348  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
18349  //first change every second comma in Input to a semicolon so can separate services but keep times with services
18350  bool EvenComma = false;
18351  for(int x = 1; x <= Input.Length(); x++)
18352  {
18353  TempStr1 = Input[x];
18354  if(TempStr1 == AnsiString(',') && EvenComma)
18355  {
18356  TempStr2 += ';';
18357  }
18358  else
18359  {
18360  TempStr2 += Input[x];
18361  }
18362  if(TempStr1 == AnsiString(','))
18363  {
18364  EvenComma = !EvenComma;
18365  }
18366  }
18367  //load up the list of services with associated times
18368  while(TempStr2.Length() > 0)
18369  {
18370  SCPos = TempStr2.Pos(';');
18371  if(SCPos > 0) //0 if not found, as won't be when only one service left
18372  {
18373  OneService = TempStr2.SubString(1, SCPos - 1);
18374  ServiceList.push_back(OneService);
18375  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
18376  }
18377  else //no semicolon so looking at last (or only) element
18378  {
18379  ServiceList.push_back(TempStr2);
18380  TempStr2 = "";
18381  }
18382  }
18383  ServiceList.sort(); // alphabetical order
18384  ServiceList.unique(); //remove duplicates
18385  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
18386 
18387  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
18388  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
18389  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
18390  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
18391 
18392  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18393  {
18394  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
18395  }
18396  SLIt3 = ServiceList.end();
18397  SLIt3--; //so points to last element
18398  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
18399  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
18400  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
18401  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
18402  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
18403  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
18404 
18405  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
18406  {
18407  SLIt = SLIt1;
18408  SLIt++; //so points to one after SLIt1
18409  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
18410  {
18411  continue; //already allocated so skip to the next
18412  }
18413  else
18414  {
18415  CommaPos1 = SLIt1->Pos(','); //can't be 0
18416  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
18417  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
18418  SpacePos = ServiceRef1.Pos(' ');
18419  RepeatNum1 = 0;
18420  if(SpacePos > 0) //otherwise it's already correct
18421  {
18422  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
18423  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
18424  if(RepeatInfo1[1] == 'F')
18425  {
18426  RepeatNum1 = 0;
18427  }
18428  else
18429  {
18430  SpacePos = RepeatInfo1.Pos(' ');
18431  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
18432  }
18433  }
18434  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
18435  //but this includes the "&0" etc so need to strip these
18436  AmpersandPos = AnsiTime1.Pos('&');
18437  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
18438 
18439  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
18440  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
18441  {
18442  throw Exception("ASCLIt1 Error in " + Input);
18443  }
18444  ServiceCallingLocsList1 = ASCLIt1->second;
18445  AmpersandPos = SLIt1->Pos('&');
18446  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18447  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
18448 
18449  SameDirectionCount = 1;
18450  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
18451  {
18452  CommaPos2 = SLIt2->Pos(','); //can't be 0
18453  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
18454  //but this contains "(First service..." etc so need to strip these
18455  SpacePos = ServiceRef2.Pos(' ');
18456  RepeatNum2 = 0;
18457  if(SpacePos > 0) //otherwise it's already correct
18458  {
18459  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
18460  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
18461  if(RepeatInfo2[1] == 'F')
18462  {
18463  RepeatNum2 = 0;
18464  }
18465  else
18466  {
18467  SpacePos = RepeatInfo2.Pos(' ');
18468  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
18469  }
18470  }
18471  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
18472  //but this includes the "&0" etc so need to strip these
18473  AmpersandPos = AnsiTime2.Pos('&');
18474  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
18475 
18476  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
18477  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
18478  {
18479  throw Exception("ASCLIt2 Error in " + Input);
18480  }
18481  ServiceCallingLocsList2 = ASCLIt2->second;
18482  //now compare the two
18483  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
18484  {
18485  int AmpersandPos = SLIt2->Pos('&');
18486  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18487  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
18488  SameDirectionCount++;
18489  }
18490  }
18491  if(SameDirectionCount > MaxNumberOfSameDirections)
18492  {
18493  MaxNumberOfSameDirections = SameDirectionCount;
18494  }
18495  }
18496  }
18497 
18498  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
18499  {
18500  //any existing direction so allocate it now
18501  AmpersandPos = SLIt3->Pos('&');
18502  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18503  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
18504  }
18505  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
18506  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18507  {
18508  //extract the DirectionMarker as an integer
18509  AmpersandPos = SLIt->Pos('&');
18510  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
18511  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
18512  DirectionMarker = DirectionMarkerString.ToInt();
18513  AnsiString DirectionSuffix = "";
18514  char c;
18515  if(DirectionMarker < 27)
18516  {
18517  c = 64 + DirectionMarker; //so 1 -> 'A'
18518  DirectionSuffix = "," + AnsiString(c);
18519  }
18520  else if(DirectionMarker < 53)
18521  {
18522  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
18523  DirectionSuffix = ",A" + AnsiString(c);
18524  }
18525  else
18526  {
18527  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
18528  }
18529  *SLIt = ServiceWithoutMarker + DirectionSuffix;
18530  }
18531  //now prepare the final consolidated output
18532  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18533  {
18534  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
18535  }
18536  if(Output.Length() > 0)
18537  {
18538  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
18539  }
18540  Utilities->CallLogPop(2216);
18541  return(Output);
18542  }
18543 
18544  catch(const Exception &e) //non error catch
18545  {
18546  AnalysisError = true;
18547  Utilities->CallLogPop(2227);
18548  return(e.Message);
18549  }
18550 }
18551 
18552 // ---------------------------------------------------------------------------
18553 
18554 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
18555 {
18556  //similar to above but doesn't include times in the input
18557  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
18558  AnsiString InternalInput = Input, Output = "", OneService = "";
18559  int CommaPos = 0;
18560  std::list<AnsiString> ServiceList;
18561  //load up the list
18562  while(InternalInput.Length() > 0)
18563  {
18564  CommaPos = InternalInput.Pos(',');
18565  if(CommaPos > 0) //0 if not found, as won't be when only one service left
18566  {
18567  OneService = InternalInput.SubString(1, CommaPos - 1);
18568  ServiceList.push_back(OneService);
18569  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
18570  }
18571  else //no comma so looking at last (or only) element
18572  {
18573  ServiceList.push_back(InternalInput);
18574  InternalInput = "";
18575  }
18576  }
18577 
18578  ServiceList.sort(); // alphabetical order
18579  ServiceList.unique(); //remove duplicates
18580  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
18581  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18582  {
18583  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
18584  }
18585  if(Output.Length() > 0)
18586  {
18587  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
18588  }
18589  Utilities->CallLogPop(2217);
18590  return(Output);
18591 }
18592 
18593 // ---------------------------------------------------------------------------
18594 
18595 
18596 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
18597  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
18598 {
18599  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
18600  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
18601 
18602  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
18603  //general list pointers, LocPtrs point to Location in the two lists
18604 
18605  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
18606  //for List1
18607  bool LocFound = false;
18608  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
18609  int IncMinutes;
18610  TDateTime FirstServiceTime;
18611 
18612  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
18613  int Ref1Target = 0, Ref1Count = 0;
18614  int SlashPos = Ref1.Pos('/');
18615  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
18616  {
18617  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
18618  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
18619  }
18620  int Ref2Target = 0, Ref2Count = 0;
18621  SlashPos = Ref2.Pos('/');
18622  if(SlashPos > 0) //if 0 leave as is
18623  {
18624  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
18625  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
18626  }
18627  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
18628  {
18629  //even if others have same names. But if there are cdt's then need to refind the correct service
18630  if((*ListPtr1) == Location) //
18631  {
18632  LocPtr1 = ListPtr1; //may be modified later
18633  LocFound = true;
18634  }
18635  if(ListPtr1->SubString(1, 3) == "%%%")
18636  {
18637  AnsiString CDTTime = ListPtr1->SubString(4, 5);
18638  //now adjust the time to correspond to the repeat if there is one
18639  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
18640  {
18641  IncMinutes = -1;
18642  FirstServiceTime = TDateTime(-1);
18643  bool BreakFlag = false;
18644  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
18645  {
18646  if(TDVIt->ServiceReference == Ref1)
18647  {
18648  if(Ref1Target > Ref1Count)
18649  {
18650  Ref1Count++;
18651  continue;
18652  }
18653  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
18654  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
18655  {
18656  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
18657  {
18658  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
18659  BreakFlag = true;
18660  break;
18661  }
18662  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
18663  {
18664  FirstServiceTime = AVIt->ArrivalTime;
18665  BreakFlag = true;
18666  break;
18667  }
18668  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
18669  {
18670  FirstServiceTime = AVIt->DepartureTime;
18671  BreakFlag = true;
18672  break;
18673  }
18674  }
18675  if(BreakFlag)
18676  {
18677  break;
18678  }
18679  }
18680  }
18681  if(IncMinutes == -1)
18682  {
18683  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18684  }
18685  if(FirstServiceTime == TDateTime(-1))
18686  {
18687  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18688  }
18689  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
18690  }
18691  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
18692  {
18693  LocFound = false;
18694  continue;
18695  }
18696  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
18697  {
18698  break;
18699  }
18700  if(Time1 > CDTTime) //not there yet so go on
18701  {
18702  LocFound = false;
18703  continue;
18704  }
18705  if(Time1 < CDTTime) //gone too far so can stop now
18706  {
18707  break;
18708  }
18709  }
18710  }
18711  if(!LocFound) //have to find it in both lists
18712  {
18713  Utilities->CallLogPop(2228);
18714  return( false);
18715  }
18716  //for List2
18717  LocFound = false;
18718  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
18719  {
18720  if((*ListPtr2) == Location)
18721  {
18722  LocPtr2 = ListPtr2; //may be modified later
18723  LocFound = true;
18724  }
18725  if(ListPtr2->SubString(1, 3) == "%%%")
18726  {
18727  AnsiString CDTTime = ListPtr2->SubString(4, 5);
18728  //now adjust the time to correspond to the repeat if there is one
18729  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
18730  {
18731  IncMinutes = -1;
18732  FirstServiceTime = TDateTime(-1);
18733  bool BreakFlag = false;
18734  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
18735  {
18736  if(TDVIt->ServiceReference == Ref2)
18737  {
18738  if(Ref2Target > Ref2Count)
18739  {
18740  Ref2Count++;
18741  continue;
18742  }
18743  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
18744  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
18745  {
18746  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
18747  {
18748  FirstServiceTime = AVIt->EventTime;
18749  BreakFlag = true;
18750  break;
18751  }
18752  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
18753  {
18754  FirstServiceTime = AVIt->ArrivalTime;
18755  BreakFlag = true;
18756  break;
18757  }
18758  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
18759  {
18760  FirstServiceTime = AVIt->DepartureTime;
18761  BreakFlag = true;
18762  break;
18763  }
18764  }
18765  if(BreakFlag)
18766  {
18767  break;
18768  }
18769  }
18770  }
18771  if(IncMinutes == -1)
18772  {
18773  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18774  }
18775  if(FirstServiceTime == TDateTime(-1))
18776  {
18777  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18778  }
18779  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
18780  }
18781  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
18782  {
18783  LocFound = false;
18784  continue;
18785  }
18786  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
18787  {
18788  break;
18789  }
18790  if(Time2 > CDTTime) //not there yet so go on
18791  {
18792  LocFound = false;
18793  continue;
18794  }
18795  if(Time2 < CDTTime) //gone too far so can stop now
18796  {
18797  break;
18798  }
18799  }
18800  }
18801  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
18802  {
18803  Utilities->CallLogPop(2229);
18804  return( false);
18805  }
18806  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
18807  //set ListPtr1 to the search start position
18808  if(Arrival)
18809  {
18810  LP1 = List1.begin();
18811  LP1--; //now points to before the first entry
18812  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
18813  {
18814  if(ListPtr1 == List1.begin())
18815  {
18816  break;
18817  }
18818  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
18819  {
18820  ListPtr1++; //point to one past the cdt
18821  break;
18822  }
18823  }
18824  //set ListPtr2 to the search start position
18825  LP2 = List2.begin();
18826  LP2--; //now points to before the first entry
18827  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
18828  {
18829  if(ListPtr2 == List2.begin())
18830  {
18831  break;
18832  }
18833  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
18834  {
18835  ListPtr2++; //point to one past the cdt
18836  break;
18837  }
18838  }
18839  //ListPtr1 & 2 now at search start position
18840  LP1 = ListPtr1;
18841  LP2 = ListPtr2;
18842  //now search forwards, i.e. for common locations before Location
18843  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
18844  {
18845  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
18846  {
18847  break;
18848  }
18849  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
18850  {
18851  break;
18852  }
18853  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
18854  {
18855  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
18856  {
18857  break;
18858  }
18859  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
18860  {
18861  break;
18862  }
18863  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
18864  {
18865  Utilities->CallLogPop(2230);
18866  return( true);
18867  }
18868  }
18869  }
18870  }
18871 
18872  //now, for the departure analysis, reset the start positions and search locations after Location
18873 
18874  else
18875  {
18876  LP1 = LocPtr1;
18877  LP1++; //start at one past the location itself
18878  LP2 = LocPtr2;
18879  LP2++;
18880  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
18881  {
18882  if(ListPtr1 == List1.end()) //reached end point so stop
18883  {
18884  break;
18885  }
18886  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
18887  {
18888  break;
18889  }
18890  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
18891  {
18892  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
18893  {
18894  break;
18895  }
18896  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
18897  {
18898  break;
18899  }
18900  if((*ListPtr1) == (*ListPtr2)) //found a common later location
18901  {
18902  Utilities->CallLogPop(2231);
18903  return( true);
18904  }
18905  }
18906  }
18907  }
18908  Utilities->CallLogPop(2232);
18909  return( false);
18910 }
18911 
18912 // ---------------------------------------------------------------------------
18913 
18914 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
18915 {
18916  // changed at v2.7.0 to show allowable exit elements
18917  if(ExitList.empty())
18918  {
18919  return("");
18920  }
18921  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
18922  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
18923  AnsiString ExitLocList = "";
18924  AllowedExits = "";
18925 
18926  unsigned int Counter = 0;
18927  for(TNumListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
18928  {
18929  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
18930  Counter++;
18931  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
18932  {
18933  ExitLocList += "\n";
18934  }
18935  }
18936  if(StartName == "")
18937  {
18938  if(ExitList.size() == 1)
18939  {
18940  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
18941  Utilities->CallLogPop(1571);
18942  return(" at " + ID);
18943  }
18944  else
18945  {
18946  Utilities->CallLogPop(1572);
18947  if(ExitList.size() < 4)
18948  {
18949  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
18950  return("");
18951  }
18952  else
18953  {
18954  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
18955  return("");
18956  }
18957  }
18958  }
18959  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
18960  {
18961  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
18962  {
18963  Utilities->CallLogPop(1570);
18964  if(ExitList.size() < 4)
18965  {
18966  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
18967  return("");
18968  }
18969  else
18970  {
18971  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
18972  return("");
18973  }
18974  }
18975  }
18976  Utilities->CallLogPop(1569);
18977  if(ExitList.size() < 4)
18978  {
18979  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
18980  return(" at " + StartName);
18981  }
18982  else
18983  {
18984  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
18985  return(" at " + StartName);
18986  }
18987 }
18988 
18989 // ---------------------------------------------------------------------------
18990 /* can't trust this as locations within a vector may not be contiguous
18991  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
18992  {
18993  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
18994  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
18995  //must be preceded by a TimeLoc departure
18996  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
18997  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
18998  {
18999  if((AVPtr + x) < TDEPtr->ActionVector.end())
19000  {
19001  AnsiString xx = (AVPtr + x)->Command;//test
19002  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
19003  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
19004  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
19005  {
19006  Utilities->CallLogPop();
19007  return false;
19008  }
19009  else if((AVPtr + x)->SequenceType == Finish)
19010  {
19011  Utilities->CallLogPop();
19012  return true;
19013  }
19014  }
19015  }
19016  Utilities->CallLogPop();
19017  return false;
19018  }
19019 */
19020 // ---------------------------------------------------------------------------
19021 
19022 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
19023 {
19024  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
19025  AnsiString FormatStr = "####0.0";
19026  AnsiString AvLateArrMins = "";
19027  AnsiString AvEarlyArrMins = "";
19028  AnsiString AvLatePassMins = "";
19029  AnsiString AvEarlyPassMins = "";
19030  AnsiString AvLateDepMins = "";
19031  AnsiString AvLateExitMins = "";
19032  AnsiString AvEarlyExitMins = "";
19033 
19034  if(LateArrivals > 0)
19035  {
19036  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
19037  }
19038  if(EarlyArrivals > 0)
19039  {
19040  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
19041  }
19042  if(LatePasses > 0)
19043  {
19044  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
19045  }
19046  if(EarlyPasses > 0)
19047  {
19048  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
19049  }
19050  if(LateDeps > 0)
19051  {
19052  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
19053  }
19054  if(LateExits > 0) //added at v2.9.1
19055  {
19056  AvLateExitMins = FormatFloat(FormatStr, (TotLateExitMins / LateExits));
19057  }
19058  if(EarlyExits > 0) //added at v2.9.1
19059  {
19060  AvEarlyExitMins = FormatFloat(FormatStr, (TotEarlyExitMins / EarlyExits));
19061  }
19062  PerfFile << '\n' << '\n' << "***************************************";
19063  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
19064 
19065  if(OnTimeArrivals != 1)
19066  {
19067  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
19068  }
19069  else
19070  {
19071  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
19072  }
19073  if(LateArrivals > 1)
19074  {
19075  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
19076  }
19077  else if(LateArrivals == 1)
19078  {
19079  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
19080  }
19081  else
19082  {
19083  PerfFile << LateArrivals << " late arrivals" << '\n';
19084  }
19085  if(EarlyArrivals > 1)
19086  {
19087  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
19088  }
19089  else if(EarlyArrivals == 1)
19090  {
19091  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
19092  }
19093  else
19094  {
19095  PerfFile << EarlyArrivals << " early arrivals" << '\n';
19096  }
19097  if(OnTimePasses != 1)
19098  {
19099  PerfFile << OnTimePasses << " on-time passes" << '\n';
19100  }
19101  else
19102  {
19103  PerfFile << OnTimePasses << " on-time pass" << '\n';
19104  }
19105  if(LatePasses > 1)
19106  {
19107  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
19108  }
19109  else if(LatePasses == 1)
19110  {
19111  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
19112  }
19113  else
19114  {
19115  PerfFile << LatePasses << " late passes" << '\n';
19116  }
19117  if(EarlyPasses > 1)
19118  {
19119  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
19120  }
19121  else if(EarlyPasses == 1)
19122  {
19123  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
19124  }
19125  else
19126  {
19127  PerfFile << EarlyPasses << " early passes" << '\n';
19128  }
19129 
19130  if(OnTimeExits != 1) //this batch added at v2.9.1
19131  {
19132  PerfFile << OnTimeExits << " on-time exits" << '\n';
19133  }
19134  else
19135  {
19136  PerfFile << OnTimeExits << " on-time exit" << '\n';
19137  }
19138  if(LateExits > 1)
19139  {
19140  PerfFile << LateExits << " late exits (average " << AvLateExitMins.c_str() << " min)" << '\n';
19141  }
19142  else if(LateExits == 1)
19143  {
19144  PerfFile << LateExits << " late exit (" << AvLateExitMins.c_str() << " min)" << '\n';
19145  }
19146  else
19147  {
19148  PerfFile << LateExits << " late exits" << '\n';
19149  }
19150  if(EarlyExits > 1)
19151  {
19152  PerfFile << EarlyExits << " early exits (average " << AvEarlyExitMins.c_str() << " min)" << '\n';
19153  }
19154  else if(EarlyExits == 1)
19155  {
19156  PerfFile << EarlyExits << " early exit (" << AvEarlyExitMins.c_str() << " min)" << '\n';
19157  }
19158  else
19159  {
19160  PerfFile << EarlyExits << " early exits" << '\n';
19161  }
19162 
19163  if(OnTimeDeps != 1)
19164  {
19165  PerfFile << OnTimeDeps << " on-time departures" << '\n';
19166  }
19167  else
19168  {
19169  PerfFile << OnTimeDeps << " on-time departure" << '\n';
19170  }
19171  if(LateDeps > 1)
19172  {
19173  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
19174  }
19175  else if(LateDeps == 1)
19176  {
19177  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
19178  }
19179  else
19180  {
19181  PerfFile << LateDeps << " late departures" << '\n';
19182  }
19183  TDateTime TempExcessLCDownTime;
19184  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
19185  {
19186 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
19187  //later perf summaries with lower values, changed at v2.8.0
19188 // {
19189  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
19190 // }
19191 /*
19192  else
19193  {
19194  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
19195  }
19196 */
19197  if(TempExcessLCDownTime > TDateTime(0))
19198  {
19199  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
19200  }
19201  }
19202 
19203  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
19204 
19205  if(ExcessLCDownMins > 0.1)
19206  {
19207  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
19208  }
19209  if(MissedStops != 1)
19210  {
19211  PerfFile << MissedStops << " missed stops" << '\n';
19212  }
19213  else
19214  {
19215  PerfFile << MissedStops << " missed stop" << '\n';
19216  }
19217  if(OtherMissedEvents != 1)
19218  {
19219  PerfFile << OtherMissedEvents << " other missed events" << '\n';
19220  }
19221  else
19222  {
19223  PerfFile << OtherMissedEvents << " other missed event" << '\n';
19224  }
19225  if(SkippedTTEvents != 1)
19226  {
19227  PerfFile << SkippedTTEvents << " skipped timetable events" << '\n';
19228  }
19229  else
19230  {
19231  PerfFile << SkippedTTEvents << " skipped timetable event" << '\n';
19232  }
19233  if(UnexpectedExits != 1)
19234  {
19235  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
19236  }
19237  else
19238  {
19239  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
19240  }
19241  if(IncorrectExits != 1)
19242  {
19243  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
19244  }
19245  else
19246  {
19247  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
19248  }
19249  if(NumFailures != 1)
19250  {
19251  PerfFile << NumFailures << " train failures" << '\n';
19252  }
19253  else
19254  {
19255  PerfFile << NumFailures << " train failure" << '\n';
19256  }
19257  if(AvHoursIntValue > 0)
19258  {
19259  if(AvHoursIntValue == 1)
19260  {
19261  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
19262  }
19263  else
19264  {
19265  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
19266  }
19267  }
19268  AnsiString AvLateMinsLocsNotReached = "";
19269 
19271  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
19272  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
19273 
19274  if(LocsNotReached > 0)
19275  {
19276  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
19277  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
19278  }
19279  if(SPADRisks != 1)
19280  {
19281  PerfFile << SPADRisks << " SPAD risks" << '\n';
19282  }
19283  else
19284  {
19285  PerfFile << SPADRisks << " SPAD risk" << '\n';
19286  }
19287  if(SPADEvents != 1)
19288  {
19289  PerfFile << SPADEvents << " SPADs" << '\n';
19290  }
19291  else
19292  {
19293  PerfFile << SPADEvents << " SPAD" << '\n';
19294  }
19295  if(Derailments != 1)
19296  {
19297  PerfFile << Derailments << " derailments" << '\n';
19298  }
19299  else
19300  {
19301  PerfFile << Derailments << " derailment" << '\n';
19302  }
19303  if(CrashedTrains != 1)
19304  {
19305  PerfFile << CrashedTrains << " crashed trains" << '\n';
19306  }
19307  else
19308  {
19309  PerfFile << CrashedTrains << " crashed train" << '\n';
19310  }
19311  PerfFile << '\n' << "***************************************" << '\n';
19312 
19313  bool DerailSPADFlag = false, CrashFlag = false;
19314 
19315  int OverallScorePercent = 100;
19316  int TotArrDepExit = 0;
19317  double TotLateMinsFactor = 1;
19318  double MissedStopAndSPADRiskFactor = 1;
19319  double NetNegFactor = 1;
19320 
19322  EarlyExits + LateExits + OnTimeExits; //exits added at v2.9.1, passes not counted
19323  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
19324  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
19325  // 'no timetabled departures... message, which was inappropriate
19326 
19327  if((SPADEvents > 0) || (Derailments > 0))
19328  {
19329  OverallScorePercent = 5; // overrides other calculations
19330  DerailSPADFlag = true;
19331  }
19332  if(CrashedTrains > 0)
19333  {
19334  OverallScorePercent = 0; // overrides other calculations
19335  CrashFlag = true;
19336  }
19337  if(OverallScorePercent == 100)
19338  {
19339  if(TotArrDepExit > 0)
19340  {
19341  TotLateMinsFactor =
19343  ((OtherMissedEvents + SkippedTTEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDepExit); //exits added at v2.9.1
19344  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
19345  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
19346  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDepExit);
19347  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
19348  // of arrivals & departures, where 4% = half, 8% = a quarter etc
19349  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
19350  // NetNegfactor: product of the above two
19351  OverallScorePercent = 100 * NetNegFactor;
19352  }
19353  }
19354  if((TotArrDepExit > 0) || DerailSPADFlag || CrashFlag)
19355  // flag condits added at v1.1.4 - see above for what the error was
19356  {
19357  AnsiString OneFailureString = ", though the failure would account for some poor performance";
19358  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
19359  AnsiString AddedString = "";
19360  if(NumFailures == 1)
19361  {
19362  AddedString = OneFailureString;
19363  }
19364  if(NumFailures > 1)
19365  {
19366  AddedString = TwoOrMoreFailureString;
19367  }
19368  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
19369  AnsiString Rating = "";
19370  if(OverallScorePercent == 100)
19371  {
19372  Rating = "Perfect!";
19373  }
19374  else if(OverallScorePercent >= 95)
19375  {
19376  Rating = "Excellent";
19377  }
19378  else if(OverallScorePercent >= 90)
19379  {
19380  Rating = "Very good";
19381  }
19382  else if(OverallScorePercent >= 80)
19383  {
19384  Rating = "Good";
19385  }
19386  else if(OverallScorePercent >= 70)
19387  {
19388  Rating = "Fair";
19389  }
19390  else if(OverallScorePercent >= 60)
19391  {
19392  Rating = "Unacceptable" + AddedString;
19393  }
19394  else if(OverallScorePercent >= 50)
19395  {
19396  Rating = "Poor" + AddedString;
19397  }
19398  else if(OverallScorePercent >= 40)
19399  {
19400  Rating = "Bad" + AddedString;
19401  }
19402  else if(OverallScorePercent >= 30)
19403  {
19404  Rating = "Very bad" + AddedString;
19405  }
19406  else if(OverallScorePercent >= 20)
19407  {
19408  Rating = "Terrible" + AddedString;
19409  }
19410  else if(OverallScorePercent >= 10)
19411  {
19412  Rating = "Appalling" + AddedString;
19413  }
19414  else if(OverallScorePercent >= 5)
19415  {
19416  if(DerailSPADFlag)
19417  {
19418  Rating = "Disastrous - potential loss of life";
19419  }
19420  // SPADs/Derailments
19421  else
19422  {
19423  Rating = "Dire" + AddedString;
19424  }
19425  }
19426  else if(OverallScorePercent < 5)
19427  {
19428  if(CrashFlag)
19429  {
19430  Rating = "Catastrophic - loss of life"; // Crashes
19431  }
19432  else
19433  {
19434  Rating = "Abysmal";
19435  }
19436  }
19437  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
19438  }
19439  else
19440  {
19441  PerfFile << "\nThere were no timetabled departures, arrivals or exits so there is insufficient information to provide a performance score or rating" << '\n';
19442  }
19443  PerfFile << '\n' << "***************************************";
19444  Utilities->CallLogPop(1736);
19445 }
19446 
19447 // ---------------------------------------------------------------------------
19448 
19450 {
19451  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
19452  for(unsigned int x = 0; x < TrainVector.size(); x++)
19453  {
19454  TTrain &Train = TrainVectorAt(58, x);
19455  if(Train.Crashed)
19456  // can't use background colours for crashed & derailed because same colour
19457  {
19458  CrashWarning = true;
19459  }
19460  else if(Train.Derailed)
19461  // can't use background colours for crashed & derailed because same colour
19462  {
19463  DerailWarning = true;
19464  }
19465  else if(Train.BackgroundColour == clSPADBackground)
19466  // use colour as that changes as soon as passes signal
19467  {
19468  SPADWarning = true;
19469  }
19470  else if(Train.BackgroundColour == clTrainFailedBackground)
19471  {
19472  TrainFailedWarning = true;
19473  }
19474  else if(Train.BackgroundColour == clCallOnBackground)
19475  // use colour as also stopped at signal
19476  {
19477  CallOnWarning = true;
19478  }
19479  else if(Train.BackgroundColour == clSignalStopBackground)
19480  // use colour to distinguish from call-on
19481  {
19482  SignalStopWarning = true;
19483  }
19484  else if(Train.BackgroundColour == clBufferAttentionNeeded)
19485  // use colour to distinguish from ordinary buffer stop
19486  {
19487  BufferAttentionWarning = true;
19488  }
19489  }
19490  Utilities->CallLogPop(1796);
19491 }
19492 
19493 // ---------------------------------------------------------------------------
19494 
19496 {
19497  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
19498 
19499  // calculate lateness for running trains
19502  for(unsigned int x = 0; x < TrainVector.size(); x++)
19503  {
19504  TTrain &Train = TrainVectorAt(64, x);
19505  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
19506  AVEntryPtr++)
19507  {
19508  if(AVEntryPtr < Train.ActionVectorEntryPtr)
19509  {
19510  continue;
19511  }
19512  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.StoppedAtLocation && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
19513  TTClockTime))
19514  {
19515  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
19517  }
19518 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
19519  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
19520  TTClockTime))
19521  {
19522  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
19523  OperatingTrainArrDep++;
19524  }
19525 */
19526  }
19527  }
19528 
19529  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
19532 
19533  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
19534  {
19535  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
19536  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
19537  int IncrementalMinutes = 0;
19538  if(AVEntryLast.FormatType == Repeat)
19539  {
19540  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
19541  }
19542  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
19543  {
19544  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
19545  if(TTOD.RunningEntry != NotStarted)
19546  {
19547  continue;
19548  }
19549  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
19550  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
19551  bool TrainOperatingFlag = false;
19552  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
19553  {
19554  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
19555  {
19556  TrainOperatingFlag = true;
19557  break;
19558  }
19559  }
19560  if(TrainOperatingFlag)
19561  {
19562  continue;
19563  }
19564  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
19565  {
19566  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
19567  }
19568  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
19569  {
19570  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
19571  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
19572  {
19573  break; // all the rest will also be greater (& default of -1 will be less)
19574  }
19575  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
19576  {
19577  break; // all the rest will also be greater (& default of -1 will be less)
19578  }
19579  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
19580  {
19581  break; // all the rest will also be greater (& default of -1 will be less)
19582  }
19583  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
19584  {
19585  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
19587  }
19588 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
19589  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
19590  {
19591  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
19592  NotStartedTrainArrDep++;
19593  }
19594 */
19595  }
19596  }
19597  }
19598  Utilities->CallLogPop(1894);
19599 }
19600 
19601 // ---------------------------------------------------------------------------
19602 
19604 // new v2.2.0 for OperatorActionPanel
19605 // clears entries then adds values for running trains then for continuation entries
19606 // dont limit size here as need to check all trains (OAListBox is limited to 20 trains in Interface.cpp)
19607 {
19608  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
19609  OpTimeToActMultiMap.clear();
19610  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
19611 
19612  if(!TrainVector.empty())
19613  // build OpTimeToActMultiMap entries for running trains
19614  {
19615  AnsiString HeadCode;
19616  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
19617  int TrainID;
19618  THCandTrainPosParam HCandTrainPosParam;
19619  for(unsigned int x = 0; x < TrainVector.size(); x++)
19620  {
19621  HeadCode = TrainVectorAt(62, x).HeadCode;
19622  TrainID = TrainVectorAt(63, x).TrainID;
19623  HCandTrainPosParam.first = HeadCode;
19624  HCandTrainPosParam.second = TrainID;
19625  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
19626  if((TimeToAct >= 0) && (TimeToAct < 59.9))
19627  // -1 indicates don't display
19628  {
19629  OpTimeToActMultiMapEntry.first = TimeToAct;
19630  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
19631  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
19632  }
19633  }
19634  }
19635 /*
19636  * class TContinuationTrainExpectationEntry
19637  {
19638  public:
19639  AnsiString Description; ///< service description
19640  AnsiString HeadCode; ///< service headcode
19641  int RepeatNumber; ///< service RepeatNumber
19642  int IncrementalMinutes; ///< Repeat separation in minutes
19643  int IncrementalDigits; ///< Repeat headcode separation
19644  int VectorPosition; ///< TrackVectorPosition for the continuation element
19645  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
19646  };
19647 
19648  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
19649  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
19650  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
19651  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
19652 */
19653 
19655  // build OpTimeToActMultiMap entries for expected trains
19656  {
19657  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
19658  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
19659  float TimeToAct = 0; // minutes
19660  int DistanceToRedSignal = 0; // metres
19661  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
19662  // used to ensure only one train displayed for a given continuation
19663  ContinuationEntryVecPosVector.clear();
19664  bool LaterTrain = false;
19667  {
19668  LaterTrain = false;
19669  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
19670  {
19671  CTEIt++;
19672  continue; // not interested in running or exited trains
19673  }
19674  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
19675  {
19676  CTEIt++;
19677  continue;
19678  // don't include trains not entered yet when a train is already on the continuation
19679  }
19680  if(!ContinuationEntryVecPosVector.empty())
19681  {
19682  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
19683  {
19684  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
19685  {
19686  LaterTrain = true;
19687  ;
19688  // skip past remaining trains waiting to enter at same point
19689  break;
19690  }
19691  }
19692  }
19693  if(LaterTrain)
19694  {
19695  CTEIt++;
19696  continue;
19697  }
19698  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
19699  AnsiString HeadCode = CTEIt->second.HeadCode;
19700  float CurrentStopTime; // set to 0 at start of function
19701  float LaterStopTime; // set to 0 at start of function
19702  float RecoverableTime; // set to 0 at start of function
19703  int AvTrackSpeed; // set to 0 at start of function
19704  int TrainID = -1; // not yet allocated for train still to enter
19705  int DistanceToExit; //not used for continuation entries
19706  THVShortPair ExitPair;
19707  bool SigControlAndCanPassRedSignal = false; // doesn't apply for a continuation
19708 
19709 //at v2.11.0 found that with *AVPtr set to ...ActionVector.at(0) below instead of ...at(1) to stop signaller control trains throwing an error (because there is no ...at(1) -
19710 //discovered with Birmingham) the LaterStopTime isn't calculated and if a train does something other than depart after an arrival it is still listed in the actions due panel -
19711 //because it just calcs the distance to the red signal and converts that to a time. So here (after v2.11.0) this new test is introduced to determine whether a train is a
19712 //signaller control train (when the ActionVector size is 1) or not (ActionVector size > 1), and the ...at(value) is set accordingly - 0 for signaller control or 1 if not.
19713 
19714  int AtValue = 1;
19715  if(CTEIt->second.TrainDataEntryPtr->ActionVector.size() == 1)
19716  {
19717  AtValue = 0;
19718  }
19719  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
19720  // EntryPos always 0 for entering at a continuation
19721  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(AtValue), //see above note
19722  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
19723  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
19724  // for a train it's the one in front of LeadElement
19725  if(AvTrackSpeed < 30)
19726  {
19727  AvTrackSpeed = 30;
19728  }
19729  if(DistanceToRedSignal == -1)
19730  {
19731  TimeToAct = 60.0;
19732  }
19733  else
19734  {
19735  int Speed = AvTrackSpeed;
19736  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
19737  if(AvTrackSpeed > MaxSpeed)
19738  {
19739  Speed = MaxSpeed;
19740  }
19741  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(0).SignallerControl) //changed to ...at(0) from at(1) at v2.11.0 as SignallerControl only valid for ..at(0)
19742  // defined in timetable as under signaller control
19743  {
19744  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
19745  LaterStopTime = 0;
19746  }
19747  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
19748  // accel & decel taken into account in
19749  // CalcDistanceToRedSignalandStopTime
19750  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
19751  // don't need CurrentStopTime or RecoverableTime for continuation entries
19752  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
19753  TimeToAct += MinsBefEnter;
19754  }
19755  THCandTrainPosParam HCandTrainPosParam;
19756  HCandTrainPosParam.first = HeadCode;
19757  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
19758  // -1-CTE... because 2nd value covers TrainID if +ve &
19759  // continuation track vector position if -ve, -1 allows for vecpos being 0
19760  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
19761  {
19762  OpTimeToActMultiMapEntry.first = TimeToAct;
19763  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
19764  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
19765  }
19766  CTEIt++;
19767  }
19768  }
19769  Utilities->CallLogPop(2081);
19770 }
19771 
19772 // ---------------------------------------------------------------------------
19773 
19775 // new for multiplayer
19776 // clears entries then adds values for running trains
19777 {
19778  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildTimeToExitMultiMap");
19779  TimeToExitMultiMap.clear();
19780  TTimeToExitMultiMapEntry TimeToExitMultiMapEntry;
19781 
19782  if(!TrainVector.empty())
19783  // build map entries for running trains
19784  {
19785  TExitInfo ExitInfo; //corresponds to TServiceInfo in Interface
19786  THVShortPair ExitPair;
19787  float TimeToExit;
19788  for(unsigned int x = 0; x < TrainVector.size(); x++)
19789  {
19791  ExitInfo.RepeatNumber = short(TrainVectorAt(81, x).RepeatNumber);
19792  ExitInfo.TimeToExitSecs = short(TrainVectorAt(77, x).TimeToExit * 60);
19793  ExitPair = TrainVectorAt(76, x).ExitPair;
19794  if((ExitInfo.TimeToExitSecs >= 3570) || (ExitInfo.TimeToExitSecs < 1)) //59.5 mins or -60 secs
19795  {
19796  ExitInfo.TimeToExitSecs = -1;
19797  }
19798  TimeToExitMultiMapEntry.first = ExitPair;
19799  TimeToExitMultiMapEntry.second = ExitInfo;
19800  TimeToExitMultiMap.insert(TimeToExitMultiMapEntry);
19801  }
19802  }
19803  Utilities->CallLogPop(2323);
19804 }
19805 
19806 // ---------------------------------------------------------------------------
19807 
19808 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
19809  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
19810  float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
19811 // new v2.2.0
19812 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
19813 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
19814 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
19815 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
19816 // aren't used - this means there is no display for the train in question
19817 {
19818  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
19819  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
19820  int DistanceToRedSignal = 0;
19821  DistanceToExit = -1;
19822  ExitPair.first = -1;
19823  ExitPair.second = -1;
19824  int CumTrackSpeed = 0;
19825  // average track speed, in case need to use in time calc
19826  int TrackSpeedCount = 0;
19827  float KmPerLocationStop;
19828  float MaxAllowableSpeed;
19829 
19830  //below added at v2.6.1
19831  if(TrainID > -1)
19832  {
19833  TTrain &Train = TrainVectorAtIdent(51, TrainID);
19834  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
19835  Train.StationStopCalculated = false;
19836  }
19837  AvTrackSpeed = 0;
19838  int CurrentElement = TrackVectorPosition;
19839  int CurrentEntryPos = TrackVectorPositionEntryPos;
19840  int NextElement;
19841  int NextEntryPos;
19842  int NextExitPos;
19843 
19844  CurrentStopTime = 0;
19845  LaterStopTime = 0;
19846  RecoverableTime = 0;
19847  if(CurrentElement == -1) // end element, no action needed
19848  {
19849  Utilities->CallLogPop(2094);
19850  return(-1);
19851  }
19852  int CurrentExitPos;
19853 
19854  // get ExitPos for first element to be measured
19855  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
19856  {
19857  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
19858  {
19859  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
19860  {
19861  CurrentExitPos = 1;
19862  }
19863  else
19864  {
19865  CurrentExitPos = 3;
19866  }
19867  }
19868  else
19869  {
19870  CurrentExitPos = 0; // trailing point
19871  }
19872  }
19873  else
19874  {
19875  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
19876  }
19877  // get CumTrackSpeed for first measured element
19878 
19879  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
19880  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
19881 
19882  // check if currently stopped at a location, and if so add the remaining dwell time
19883  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
19884  if(TrainID > -1)
19885  // -1 for a continuation and can't be at a location as not yet entered
19886  {
19887  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
19889  // this used to deduct from RecoverableTime when arrive at a location
19890  if(Train.StoppedAtLocation)
19891  {
19892  if(Train.StoppedForTrainInFront)
19893  {
19894  Utilities->CallLogPop(2082);
19895  return(-1); // no action needed
19896  }
19897  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
19898  {
19899  Utilities->CallLogPop(2083);
19900  return(-1); // not due a departure so no action needed
19901  }
19902  else // due a departure
19903  {
19904  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
19905  // can't convert a TDateTime to a float directly
19906  CurrentStopTime = float(TimeToDepart);
19907  AVPtr++;
19908  }
19909  }
19910  }
19911  // check if CurrentElement is a red signal, but ok if autosig route after
19912  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
19913  // ok if autosig route after red signal
19914  {
19915  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
19916  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
19917  int RouteNumber; // holder for referenced value, not used
19918  if(AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
19919  {
19920  Utilities->CallLogPop(2078);
19921  return(-1);
19922  }
19923  else if(SigControlAndCanPassRedSignal)
19924  // ignore signal and increment CurrentElement to NextElement
19925  {
19926  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
19927  {
19928  if((NextEntryPos == 0) || (NextEntryPos == 2))
19929  // leading entry point
19930  {
19931  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
19932  {
19933  NextExitPos = 1;
19934  }
19935  else
19936  {
19937  NextExitPos = 3;
19938  }
19939  }
19940  else
19941  {
19942  NextExitPos = 0; // trailing entry point
19943  }
19944  }
19945  else
19946  {
19947  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
19948  }
19949  CurrentElement = NextElement;
19950  CurrentEntryPos = NextEntryPos;
19951  CurrentExitPos = NextExitPos;
19952  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
19953  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
19954  }
19955  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
19956  // give 'NOW' indication after allowed to pass red signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
19957  {
19958  Utilities->CallLogPop(2084);
19959  return(0);
19960  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
19961  }
19962  }
19963  int LaterStopNumber = 0;
19964  int x = 0;
19965  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
19966 
19967  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
19968  // not red signal next (in fwd direction) so enter loop to calc CumLength
19969  {
19970  x++; // added in v2.4.0 as above
19971  if(x > 5000)
19972  {
19973  Utilities->CallLogPop(2120);
19974  return(-1);
19975  }
19976  if(CurrentEntryPos > 1)
19977  {
19978  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
19979  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
19980  }
19981  else
19982  {
19983  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
19984  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
19985  }
19986  TrackSpeedCount++;
19987 
19988  //added for multiplayer - exiting at a continuation and continuation length already added
19989  if((Track->TrackElementAt(1407, CurrentElement).TrackType == Continuation) && (Track->TrackElementAt(1408, CurrentElement).Config[CurrentExitPos] == End))
19990  {
19991  DistanceToExit = DistanceToRedSignal; //don't need to exit function here as will exit when find that the next Conn value is -1
19992  ExitPair.first = Track->TrackElementAt(1409, CurrentElement).HLoc;
19993  ExitPair.second = Track->TrackElementAt(1410, CurrentElement).VLoc;
19994  //here repeat calcs for MaxAllowableSpeed & AvTrackSpeed as done at end for stop signal
19995  //need here as next element will be -1 so will exit before calcs at end
19996  if(TrackSpeedCount > 0)
19997  {
19998  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
19999  }
20000  else // shouldn't reach here but include to prevent divide by zero error
20001  {
20002  if(CurrentEntryPos > 1)
20003  {
20004  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
20005  }
20006  else
20007  {
20008  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
20009  }
20010  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
20011  }
20012  //calc AvTrackSpeed
20013  if(LaterStopNumber > 0)
20014  {
20015  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
20016  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
20017  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
20018  // average line speed/2 (for half distance accelerating and half decelerating.
20019  }
20020  else
20021  {
20022  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
20023  // using linear trendline for accel & decel distance at various speeds
20024  // at half braking, speed never < 60 using this
20025  }
20026  if(AvTrackSpeed > MaxAllowableSpeed)
20027  {
20028  AvTrackSpeed = MaxAllowableSpeed;
20029  }
20030  }
20031 
20032  // added at v2.6.1 to find DistanceToStationStop for trains running early
20033  if(TrainID > -1) //can ignore continuation entries as these don't run early
20034  {
20035  TTrain &Train = TrainVectorAtIdent(52, TrainID);
20036  if(!Train.StationStopCalculated)
20037  {
20038  if(Train.TrainMode == Timetable)
20039  {
20040  bool StopRequired = false;
20041  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
20042  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
20043  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
20044  {
20045  // no need to add in the length of element to CumulativeLength
20046  if(StopRequired)
20047  {
20048  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
20049  Train.StationStopCalculated = true; //don't want to update it with later stops
20050  }
20051  }
20052  }
20053  }
20054  }
20055  // check for train in front, but if on a bridge on other track then ok
20056  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
20057  int TrainOnElement;
20058  if(TE.TrackType != Bridge)
20059  {
20060  TrainOnElement = TE.TrainIDOnElement;
20061  }
20062  else
20063  {
20064  if(CurrentEntryPos > 1)
20065  {
20066  TrainOnElement = TE.TrainIDOnBridgeTrackPos23;
20067  }
20068  else
20069  {
20070  TrainOnElement = TE.TrainIDOnBridgeTrackPos01;
20071  }
20072  }
20073  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
20074  // train in front before red signal
20075  {
20076  Utilities->CallLogPop(2085);
20077  return(-1);
20078  }
20079  // add to stoptime if required
20080  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
20081  {
20082  double StopTimeDouble;
20083  while(AVPtr->FormatType == PassTime)
20084  {
20085  AVPtr++; // skip past any passes
20086  }
20087  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
20088  (AVPtr->FormatType == TimeTimeLoc)))
20089  // stop due here so calc dwell time & advance Ptr
20090  {
20091  if(AVPtr->FormatType == TimeTimeLoc)
20092  {
20093  LaterStopNumber++;
20094  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
20095  if(StopTimeDouble < 0.5)
20096  {
20097  StopTimeDouble = 0.5;
20098  }
20099  // at least 30 secs delay at station
20100  // can't convert a TDateTime to a float directly
20101  LaterStopTime += float(StopTimeDouble);
20102  RecoverableTime += StopTimeDouble - 0.5;
20103  if((LaterStopNumber == 1) && (TrainID > -1))
20104  {
20105  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
20106  }
20107  AVPtr++;
20108  }
20109  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
20110  {
20111  if((AVPtr + 1)->FormatType == TimeLoc)
20112  // must be a departure
20113  {
20114  LaterStopNumber++;
20115  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
20116  // can't convert a TDateTime to a float directly
20117  if(StopTimeDouble < 0.5)
20118  {
20119  StopTimeDouble = 0.5;
20120  }
20121  // at least 30 secs delay at station
20122  LaterStopTime += float(StopTimeDouble);
20123  RecoverableTime += StopTimeDouble - 0.5;
20124  if((LaterStopNumber == 1) && (TrainID > -1))
20125  {
20126  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
20127  }
20128  AVPtr++;
20129  AVPtr++;
20130  }
20131  else // not a departure, does something else at the location so no calculation needed
20132  {
20133  Utilities->CallLogPop(2086);
20134  return(-1);
20135  }
20136  }
20137  }
20138  }
20139  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
20140  if(NextElement == -1) // reached end element, no action needed
20141  {
20142  Utilities->CallLogPop(2077);
20143  return(-1);
20144  }
20145  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
20146  // get NextExitPos
20147  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
20148  {
20149  if((NextEntryPos == 0) || (NextEntryPos == 2))
20150  // leading entry point
20151  {
20152  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
20153  {
20154  NextExitPos = 1;
20155  }
20156  else
20157  {
20158  NextExitPos = 3;
20159  }
20160  }
20161  else
20162  {
20163  NextExitPos = 0; // trailing entry point
20164  }
20165  }
20166  else
20167  {
20168  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
20169  }
20170  CurrentElement = NextElement;
20171  CurrentEntryPos = NextEntryPos;
20172  CurrentExitPos = NextExitPos;
20173  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
20174  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
20175  }
20176  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
20177  // ok if autosig route after red signal, no action needed
20178  {
20179  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
20180  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
20181  int RouteNumber; // holder for referenced value, not used
20182  if(AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
20183  {
20184  Utilities->CallLogPop(2095);
20185  return(-1);
20186  }
20187  }
20188 
20189  if(TrackSpeedCount > 0)
20190  {
20191  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
20192  }
20193  else // shouldn't reach here but include to prevent divide by zero error
20194  {
20195  if(CurrentEntryPos > 1)
20196  {
20197  MaxAllowableSpeed = Track->TrackElementAt(1433, CurrentElement).SpeedLimit23;
20198  }
20199  else
20200  {
20201  MaxAllowableSpeed = Track->TrackElementAt(1434, CurrentElement).SpeedLimit01;
20202  }
20203  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
20204  }
20205 
20206  if(LaterStopNumber > 0)
20207  {
20208  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
20209  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
20210  }
20211  else
20212  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
20213  // average line speed/2 (for half distance accelerating and half decelerating.
20214  {
20215  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
20216  // using linear trendline for accel & decel distance at various speeds
20217  // at half braking, speed never < 60 using this
20218  }
20219  if(AvTrackSpeed > MaxAllowableSpeed)
20220  {
20221  AvTrackSpeed = MaxAllowableSpeed;
20222  }
20223  Utilities->CallLogPop(2096);
20224  return(DistanceToRedSignal);
20225 }
20226 
20227 // ---------------------------------------------------------------------------
20228 // end of TTrainController entries
20229 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:8456
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17648
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:136
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:377
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:52
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:14403
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:343
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:342
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:477
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:683
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:71
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1690
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:899
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:174
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:14426
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:14071
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:364
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel
Definition: TrainUnit.cpp:19603
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:791
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:15468
TTrain::CheckNewServiceDepartureTime
AnsiString CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
called during FloatingLabelNextString to find the next service departure time
Definition: TrainUnit.cpp:7059
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:91
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:53
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:995
Create
@ Create
Definition: TrainUnit.h:52
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:770
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:18654
Arrive
@ Arrive
Definition: TrainUnit.h:52
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:52
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:341
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:383
Depart
@ Depart
Definition: TrainUnit.h:52
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:716
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:11274
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:714
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:423
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:780
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:281
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7235
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1607
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:53
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:905
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1619
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:711
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:142
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:764
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1631
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:66
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:66
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:494
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:731
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6217
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:67
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:427
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:71
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:19403
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:13349
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:150
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:364
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:981
TTrain::TrainFailed
bool TrainFailed
added at v2.4.0 to indicate failure
Definition: TrainUnit.h:409
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
DisplayUnit.h
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:964
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:777
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:337
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:960
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:18914
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:7332
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:385
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:468
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:364
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6331
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:777
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:9674
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:317
THVShortPair
std::pair< short, short > THVShortPair
Definition: InterfaceUnit.h:81
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:896
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route that the continuation is in
Definition: TrainUnit.h:705
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:15199
TExitInfo::TimeToExitSecs
short TimeToExitSecs
Definition: TrainUnit.h:110
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:836
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:987
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:826
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:994
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:185
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:18444
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:777
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:11227
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1611
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:8428
TNumListIterator
TNumList::iterator TNumListIterator
Definition: TrainUnit.h:97
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:136
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:947
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:727
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:15214
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:265
Intermediate
@ Intermediate
Definition: TrainUnit.h:77
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:945
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6146
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:961
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3499
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:665
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:66
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:490
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:450
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:9636
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:54
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:483
NotStarted
@ NotStarted
Definition: TrainUnit.h:88
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:10803
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:830
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:773
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:344
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:719
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:371
TTrainDataEntry
Contains all data for a single timetable service entry.
Definition: TrainUnit.h:207
LeadMid
@ LeadMid
Definition: TrainUnit.h:298
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:820
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:18918
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:14151
TTrainController::RebuildTimeToExitMultiMap
void RebuildTimeToExitMultiMap(int Caller)
new for multiplayer
Definition: TrainUnit.cpp:19774
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:959
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6352
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:723
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:1001
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:252
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:997
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:215
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:840
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:797
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:713
TTrain::SkippedDeparture
bool SkippedDeparture
used to indicate that a departure is still awaited when later timetabled events are to be skipped
Definition: TrainUnit.h:325
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:8863
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:15317
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:298
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:222
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:877
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:15255
StartNew
@ StartNew
Definition: TrainUnit.h:66
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:52
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:298
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:784
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:345
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:53
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:438
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:713
TTrainController::TimeToExitMultiMap
TTimeToExitMultiMap TimeToExitMultiMap
Map of times to exit & exit coordinates.
Definition: TrainUnit.h:980
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:18192
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:10835
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:970
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5827
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:497
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:186
TTrain
Definition: TrainUnit.h:304
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:967
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:10854
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TTrackElement::TrainIDOnBridgeTrackPos23
int TrainIDOnBridgeTrackPos23
Set to the TrainID value when a train is present on the element, bridges can have two trains present ...
Definition: TrackUnit.h:152
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:11433
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:872
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:250
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:854
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:474
GapJump
@ GapJump
Definition: TrackUnit.h:65
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:988
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:823
NoSequence
@ NoSequence
Definition: TrainUnit.h:77
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:148
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:148
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:405
Finish
@ Finish
Definition: TrainUnit.h:77
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:6097
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:9267
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:364
TTrackElement::TempTrackMarker23
bool TempTrackMarker23
Utility markers for program use.
Definition: TrackUnit.h:138
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1658
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:364
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:358
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:225
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:686
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:362
TExitInfo::TExitInfo
TExitInfo()
Definition: TrainUnit.cpp:62
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:15296
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:816
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:323
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:175
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:768
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:454
TExitInfo::RepeatNumber
short RepeatNumber
Definition: TrainUnit.h:109
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:701
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:15019
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1374
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:6079
End
@ End
Definition: TrackUnit.h:75
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:395
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:6933
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:8775
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:339
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:10528
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:67
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:299
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:401
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:15379
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:124
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:955
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:788
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:1005
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:828
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:209
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:817
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:417
SignalPost
@ SignalPost
Definition: TrackUnit.h:65
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:949
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled event, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:458
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:144
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1635
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:347
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:11898
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:977
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:209
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5425
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:150
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:831
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:754
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:881
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:415
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:74
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:470
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:64
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:826
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:791
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:893
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1878
TExitInfo
Definition: TrainUnit.h:106
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:474
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:824
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:965
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:66
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:93
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:136
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:474
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:16176
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:943
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:448
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:968
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:10172
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:725
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:364
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:446
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:81
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:993
Pass
@ Pass
Definition: TrainUnit.h:54
TTrain::SkipPtrValue
int SkipPtrValue
stores the pointer increment from first action in ActionVector for skipped actions when a departure i...
Definition: TrainUnit.h:373
TNumList
std::list< int > TNumList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:93
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:777
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:739
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:72
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:3024
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11251
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:808
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:70
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:980
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:481
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:757
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:862
TTrack::ResetAllTrainIDElements
void ResetAllTrainIDElements(int Caller)
Track elements have members that indicates whether and on what track a train is present (TrainIDOnEle...
Definition: TrackUnit.cpp:7607
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:953
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:699
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:352
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:317
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:986
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:803
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:8440
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:989
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:211
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:66
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:221
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6789
TTrainController::MTBFHours
double MTBFHours
Mean time between failures in timetable clock hours.
Definition: TrainUnit.h:794
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:17368
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:462
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:14688
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:10669
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:807
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:311
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3232
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:1000
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:456
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:5015
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2545
RearSplit
@ RearSplit
Definition: TrainUnit.h:52
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:134
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:54
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:801
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:649
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:950
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:801
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:973
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:134
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:54
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10563
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:2956
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6761
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:14786
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:54
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:867
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:13449
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:165
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:822
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:846
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:379
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3449
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:946
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:8504
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:329
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:8250
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:7529
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:2948
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:807
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:477
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:452
NewService
@ NewService
Definition: TrainUnit.h:52
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:15532
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1474
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:474
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:381
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1618
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:369
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:223
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:219
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:814
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:170
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:990
Crossover
@ Crossover
Definition: TrackUnit.h:65
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4624
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:719
AtLocation
@ AtLocation
Definition: TrainUnit.h:72
Signal
@ Signal
Definition: TrackUnit.h:75
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:952
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:983
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:772
TTrackElement::TrainIDOnBridgeTrackPos01
int TrainIDOnBridgeTrackPos01
Definition: TrackUnit.h:152
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17885
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:860
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:18554
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1619
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:454
Exited
@ Exited
Definition: TrainUnit.h:88
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:10514
TTrain::ActionsSkippedFlag
bool ActionsSkippedFlag
prevents any further skipping until after the next departure
Definition: TrainUnit.h:327
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:15230
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:7816
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:17711
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:1002
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2162
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:474
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:59
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1640
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:942
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:474
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:737
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:313
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:779
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:52
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:425
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:791
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:972
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:948
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:364
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:66
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:984
Leave
@ Leave
Definition: TrainUnit.h:52
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:833
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:749
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:775
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel
Definition: TrainUnit.h:444
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:393
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:2929
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:951
NoFormat
@ NoFormat
Definition: TrainUnit.h:66
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:787
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:66
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2474
TTrain::TimeToExit
float TimeToExit
in minutes: new for multiplayer, -1 = > 60 mins
Definition: TrainUnit.h:442
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:474
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3478
Enter
@ Enter
Definition: TrainUnit.h:52
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:514
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7822
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:958
TTrain::NewTrainService
void NewTrainService(int Caller)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6291
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:15279
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:436
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:276
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:13979
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:729
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:8898
Timetable
@ Timetable
Definition: TrainUnit.h:60
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:14358
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:6844
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:785
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:11102
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:764
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:132
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:17554
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:65
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:777
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:269
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:19495
TDisplay::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: DisplayUnit.cpp:521
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:82
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:431
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:747
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:263
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:783
Terminate
@ Terminate
Definition: TrainUnit.h:52
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:791
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
< map of coupled continuations
Definition: TrackUnit.h:776
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:474
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:146
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:488
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:366
Running
@ Running
Definition: TrainUnit.h:88
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:285
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3575
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:894
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13321
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:14798
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:15555
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:446
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:11322
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:709
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:231
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:834
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:419
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2359
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:18569
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:850
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1605
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:9356
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:8951
TTrainController::ControllerCheckNewServiceDepartureTime
AnsiString ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::CheckNewServiceDepartureTime for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:9865
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:844
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:975
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:127
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:516
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:8962
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:992
TTrain::ExitPair
THVShortPair ExitPair
H & V coordinates of the exit element related to TimeToExit, new for multiplayer.
Definition: TrainUnit.h:460
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1692
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:791
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:11315
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3428
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3128
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:454
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
new v2.2.0 (DistanceToExit added for multiplayer), calcs distances to red signal & exit,...
Definition: TrainUnit.cpp:19808
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:784
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:72
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:54
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1603
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:8397
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:2992
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:122
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:810
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:15401
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:266
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:969
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:14765
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:144
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:852
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:475
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4561
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:278
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in OAListB...
Definition: TrainUnit.cpp:9690
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:53
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:354
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:403
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:11398
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:806
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:96
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:8976
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:152
TTrainController::SkippedTTEvents
int SkippedTTEvents
Definition: TrainUnit.h:829
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:901
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:434
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:745
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:160
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1673
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:213
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:129
TTimeToExitMultiMapEntry
std::pair< THVShortPair, TExitInfo > TTimeToExitMultiMapEntry
Definition: TrainUnit.h:116
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:974
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:227
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:146
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:799
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:883
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:550
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:5038
TTrain::TRSTime
TDateTime TRSTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:456
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1609
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:57
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1619
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:140
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:333
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:998
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:692
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6591
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:690
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:9340
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:892
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:815
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:350
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:15568
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:748
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:963
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:150
Points
@ Points
Definition: TrackUnit.h:65
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:10907
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:738
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:391
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:809
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:13878
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6632
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:389
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:791
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:832
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:54
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3423
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:982
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:397
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:492
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all points appropriately. Also called when a new train is added at...
Definition: TrackUnit.cpp:16815
Continuation
@ Continuation
Definition: TrackUnit.h:65
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:777
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7580
GraphicUnit.h
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:746
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:825
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:466
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:10044
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:360
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:19022
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3086
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:897
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:54
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:760
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:900
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:996
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:9721
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:87
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:472
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:659
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:723
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6534
TActionVectorEntry::ExitList
TNumList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:138
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:827
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:479
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:263
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:180
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:499
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:54
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:979
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:19233
TTrackElement::TempTrackMarker01
bool TempTrackMarker01
Definition: TrackUnit.h:138
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrainController::GetServiceFromVector
TTrainDataEntry GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
Definition: TrainUnit.cpp:18276
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:877
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:971
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:10960
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:315
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:277
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:702
TTrainController::SingleServiceOutput
void SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
Outputs the single service vector for train direction analysis purposes in timetable conflict analysi...
Definition: TrainUnit.cpp:18169
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:701
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:8360
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:65
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:777
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:320
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:746
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:187
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:721
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:966
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:18342
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:18298
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:781
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:124
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:282
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
Start
@ Start
Definition: TrainUnit.h:77
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:904
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:331
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:51
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:701
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:364
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:819
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:1003
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:82
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:9307
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:14712
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:7108
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:17568
TTrain::AValue
double AValue
this is a useful shorthand value in calculating speeds and transit times in SetTrainMovementValues [=...
Definition: TrainUnit.h:411
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5478
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:18312
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:9325
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:148
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13328
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:340
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:15361
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:185
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:155
NoLocation
@ NoLocation
Definition: TrainUnit.h:72
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:914
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:387
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:217
NoMode
@ NoMode
Definition: TrainUnit.h:60
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:821
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:11746
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:99
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:750
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:791
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:66
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:176
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:195
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:842
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:813
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:789
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:531
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5122
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:474
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:9709
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:962
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:140
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:297
FailEntryRouteSetAgainst
@ FailEntryRouteSetAgainst
Definition: TrainUnit.h:44
TTrain::TrainSkippedEvents
int TrainSkippedEvents
stores the pointer increment from the current action in ActionVector for skipped actions when a depar...
Definition: TrainUnit.h:375
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:762
TExitInfo::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:108
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable - one entry per timetable service entry (the object i...
Definition: TrainUnit.h:240
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:14392
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7319
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:440
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:688
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1613
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:144
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:356
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:752
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:956
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:999
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:748
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:3050
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled event entries, null on creation
Definition: TrainUnit.h:124
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:146
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:407
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:55
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:991
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:124
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4747
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:721
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6694
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:148
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:399
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:838
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:791
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:954
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6343
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:723
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:66
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:53
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:751
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:19449
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:717
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:66
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:805
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:985
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:856
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:703
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:128
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:5027
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:429
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:148
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:707
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:335
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:734
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:727
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:67
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:818
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:2974
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:89
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:642
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1052
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:976
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:18596
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6562
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3470
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:130
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2562
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:190
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:209
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:6875
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:338
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:75
Repeat
@ Repeat
Definition: TrainUnit.h:67
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:944
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:13337
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:670
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:38
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:791
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:157
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:66
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:150
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:475
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:978
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:728
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:867
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:248
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5788
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:18338
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:53
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:142
Signaller
@ Signaller
Definition: TrainUnit.h:60
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:485
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:902
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:183
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:890
Bridge
@ Bridge
Definition: TrackUnit.h:65
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:957
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:280
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:364
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:76
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:421
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:77
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:524
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:65
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:134
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:124